chore(providers): reconcile the xKiro counts with the chipotle removal

The branch merged the release tip twice: the first pass carried a stale
origin/release/v3.8.51 that predated #13913, so every derived count was
computed against a provider that had already been retired.

Reconciled against the current tip:

- Provider count is 359 (tip 358 after the chipotle/pepper removal, plus
  xKiro). README, AGENTS.md, llm.txt and its 65 locale mirrors, package.json
  and the six count-bearing SVGs now carry that number; check:docs-counts
  passes with no strict drift.
- docs/reference/PROVIDER_REFERENCE.md regenerated from the live modules.
- RESERVED_PREFIX_COUNT 412 -> 413: xKiro registers id "xkiro" with no
  separate alias, so it adds exactly one REGISTRY member.
This commit is contained in:
diegosouzapw
2026-09-17 13:32:53 -03:00
107 changed files with 321 additions and 783 deletions

View File

@@ -44,7 +44,7 @@ function body(model: string) {
// connections each test seeds, which is what these assertions are actually
// about (LKGP pinning and variant pool resolution) — rather than weakening the
// assertions to accept whatever the open pool happens to pick.
const NO_AUTH_PROVIDER_IDS = ["opencode", "duckduckgo-web", "chipotle", "veoaifree-web", "auggie"];
const NO_AUTH_PROVIDER_IDS = ["opencode", "duckduckgo-web", "veoaifree-web", "auggie"];
test.beforeEach(async () => {
BaseExecutor.RETRY_CONFIG.delayMs = 0;

View File

@@ -105,11 +105,6 @@
"configSource": "cheaperinference",
"provider": "cheaperinference"
},
"chipotle": {
"className": "ChipotleExecutor",
"configSource": "<custom-config>",
"provider": "chipotle"
},
"cinf": {
"className": "CheaperInferenceExecutor",
"configSource": "cheaperinference",
@@ -490,11 +485,6 @@
"configSource": "opencode-zen",
"provider": "opencode-zen"
},
"pepper": {
"className": "ChipotleExecutor",
"configSource": "<custom-config>",
"provider": "chipotle"
},
"perplexity-web": {
"className": "PerplexityWebExecutor",
"configSource": "<custom-config>",
@@ -676,6 +666,6 @@
"provider": "zai-web"
}
},
"keyCount": 135,
"keyCount": 133,
"sharedInstances": []
}

View File

@@ -980,29 +980,6 @@
"stream": "https://chenzk.top/v1/chat/completions"
}
},
"chipotle": {
"format": "openai",
"headers": {
"apiKey": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
},
"nonStream": {
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
},
"oauth": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
}
},
"url": {
"nonStream": "https://amelia.chipotle.com",
"stream": "https://amelia.chipotle.com"
}
},
"chutes": {
"format": "openai",
"headers": {

View File

@@ -18,14 +18,14 @@ test("#4976 400 with rate-limit text (MiMoCode) → fallback with RATE_LIMIT_EXC
"Detected high-frequency non-compliant requests from you.",
0,
null,
"chipotle"
"mimocode"
);
assert.equal(res.shouldFallback, true);
assert.equal(res.reason, RateLimitReason.RATE_LIMIT_EXCEEDED);
});
test("#4976 400 with Chinese rate-limit text → fallback with RATE_LIMIT_EXCEEDED", () => {
const res = checkFallbackError(400, "检测到您的请求频率过高,请稍后再试", 0, null, "chipotle");
const res = checkFallbackError(400, "检测到您的请求频率过高,请稍后再试", 0, null, "mimocode");
assert.equal(res.shouldFallback, true);
assert.equal(res.reason, RateLimitReason.RATE_LIMIT_EXCEEDED);
});

View File

@@ -5,7 +5,7 @@ import { BaseExecutor } from "../../open-sse/executors/base.ts";
/**
* Generic BaseExecutor consumer — no buildHeaders() override — representing
* every provider (xai, cliproxyapi, chipotle, mimocode, ninerouter,
* every provider (xai, cliproxyapi, mimocode, ninerouter,
* gitlab, ...) that relies on BaseExecutor.buildHeaders() as-is.
*
* Regression guard for #8467/#8493: resolveEffectiveKey() already rotates to

View File

@@ -1,77 +0,0 @@
import { describe, it } from "node:test";
import assert from "node:assert";
import {
ChipotleExecutor,
randomServerId,
randomSessionId,
} from "../../open-sse/executors/chipotle.ts";
const executor = new ChipotleExecutor();
describe("ChipotleExecutor", () => {
it("buildHeaders returns static headers", () => {
const headers = (executor as any).buildHeaders({});
assert.strictEqual(headers["Content-Type"], "application/json");
});
it("buildUrl returns Amelia endpoint", () => {
const url = executor.buildUrl("pepper-1", false);
const parsed = new URL(url);
assert.strictEqual(parsed.hostname, "amelia.chipotle.com");
});
// Regression guard for the node:crypto import — randomInt is NOT on the Web
// Crypto global, so a bare `crypto.randomInt` would throw at WS-connect time.
it("randomServerId yields a 3-digit numeric string (crypto.randomInt available)", () => {
for (let i = 0; i < 50; i++) {
const id = randomServerId();
assert.match(id, /^\d{3}$/, `expected 3 digits, got "${id}"`);
assert.ok(Number(id) >= 0 && Number(id) <= 999);
}
});
it("randomSessionId yields 32 hex chars (crypto.randomUUID available)", () => {
const id = randomSessionId();
assert.match(id, /^[0-9a-f]{32}$/, `expected 32 hex chars, got "${id}"`);
assert.notStrictEqual(randomSessionId(), randomSessionId());
});
it("transformRequest passes model through", () => {
const result = (executor as any).transformRequest(
"pepper-1",
{ model: "pepper-1", messages: [{ role: "user", content: "hi" }] },
false,
);
assert.strictEqual(result.model, "pepper-1");
});
it("returns 499 on pre-aborted signal", async () => {
const controller = new AbortController();
controller.abort(new Error("cancelled"));
const result = await executor.execute({
model: "pepper-1",
body: { messages: [{ role: "user", content: "hi" }], stream: false },
stream: false,
signal: controller.signal,
credentials: {},
log: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
});
assert.strictEqual((result as any).response.status, 499);
});
it("is registered in executor index", async () => {
const { getExecutor } = await import("../../open-sse/executors/index.ts");
const exec = await getExecutor("chipotle");
assert.ok(exec, "chipotle executor should be registered");
assert.ok(exec instanceof ChipotleExecutor);
});
it("pepper alias works", async () => {
const { getExecutor } = await import("../../open-sse/executors/index.ts");
const exec = await getExecutor("pepper");
assert.ok(exec, "pepper alias should be registered");
assert.ok(exec instanceof ChipotleExecutor);
});
});

View File

@@ -11,7 +11,7 @@ import { classifyProviderError } from "../../open-sse/services/errorClassifier.t
test("#6345: no-credential provider 'Request blocked'/access_denied 403 -> recoverable (null), not FORBIDDEN", () => {
const body = { error: "Request blocked", type: "access_denied" };
assert.equal(classifyProviderError(403, body, "chipotle"), null);
assert.equal(classifyProviderError(403, body, "duckduckgo-web"), null);
});
test("control: apikey-provider bare 403 still recoverable (null) — no regression", () => {

View File

@@ -254,7 +254,6 @@ test("public identifier vocabulary preserves current internal machine-readable c
"BLACKBOX_RATE_LIMIT",
"abort",
"ABORTED",
"CHIPOTLE_ERROR",
"premium_model_requires_key",
"GROK_ERROR",
"TLS_CLIENT_UNAVAILABLE",

View File

@@ -17,7 +17,7 @@ test("free onboarding candidates come from the no-auth registry and exclude loca
assert.ok(ids.includes("opencode"));
assert.ok(ids.includes("duckduckgo-web"));
assert.ok(!ids.includes("felo-web"));
assert.ok(ids.includes("chipotle"));
assert.ok(ids.includes("cloudflare-playground"));
assert.ok(ids.includes("aihorde"));
assert.ok(!ids.includes("devin-cli-agentic"));
assert.ok(!ids.includes("auggie"));

View File

@@ -10,7 +10,7 @@ test("batch setup creates missing providers, skips existing ones, and is retry-s
const existing = [{ provider: "opencode", name: "My customized OpenCode" }];
const created: Array<{ provider: string; name: string }> = [];
const candidates = getEligibleFreeOnboardingProviders();
const requestedIds = ["opencode", "chipotle"];
const requestedIds = ["opencode", "cloudflare-playground"];
const first = await setupFreeProviderConnections({
requestedIds,
@@ -33,14 +33,20 @@ test("batch setup creates missing providers, skips existing ones, and is retry-s
assert.deepEqual(first.results, [
{ providerId: "opencode", status: "skipped", reason: "already-configured" },
{ providerId: "chipotle", status: "created", connectionId: "created-chipotle" },
{
providerId: "cloudflare-playground",
status: "created",
connectionId: "created-cloudflare-playground",
},
]);
assert.deepEqual(second.results, [
{ providerId: "opencode", status: "skipped", reason: "already-configured" },
{ providerId: "chipotle", status: "skipped", reason: "already-configured" },
{ providerId: "cloudflare-playground", status: "skipped", reason: "already-configured" },
]);
assert.deepEqual(existing, [{ provider: "opencode", name: "My customized OpenCode" }]);
assert.deepEqual(created, [{ provider: "chipotle", name: "Chipotle Pepper AI (Free)" }]);
assert.deepEqual(created, [
{ provider: "cloudflare-playground", name: "Cloudflare AI Playground" },
]);
});
test("batch setup rejects unknown or ineligible IDs before creating anything", async () => {
@@ -63,13 +69,14 @@ test("batch setup rejects unknown or ineligible IDs before creating anything", a
test("partial failures are reported per provider and can be retried", async () => {
const created = new Set<string>();
let chipotleAttempts = 0;
let cloudflarePlaygroundAttempts = 0;
const input = {
requestedIds: ["opencode", "chipotle"],
requestedIds: ["opencode", "cloudflare-playground"],
candidates: getEligibleFreeOnboardingProviders(),
listExisting: async () => [...created].map((provider) => ({ provider })),
create: async ({ provider }: { provider: string }) => {
if (provider === "chipotle" && chipotleAttempts++ === 0) throw new Error("upstream detail");
if (provider === "cloudflare-playground" && cloudflarePlaygroundAttempts++ === 0)
throw new Error("upstream detail");
created.add(provider);
return { id: `created-${provider}` };
},
@@ -80,10 +87,14 @@ test("partial failures are reported per provider and can be retried", async () =
assert.deepEqual(first.results, [
{ providerId: "opencode", status: "created", connectionId: "created-opencode" },
{ providerId: "chipotle", status: "failed", reason: "Failed to create provider" },
{ providerId: "cloudflare-playground", status: "failed", reason: "Failed to create provider" },
]);
assert.deepEqual(retry.results, [
{ providerId: "opencode", status: "skipped", reason: "already-configured" },
{ providerId: "chipotle", status: "created", connectionId: "created-chipotle" },
{
providerId: "cloudflare-playground",
status: "created",
connectionId: "created-cloudflare-playground",
},
]);
});

View File

@@ -0,0 +1,39 @@
// #13131 / #4037 — chipotle/pepper's upstream (amelia.chipotle.com) 404s on every route
// (verified live 2026-09-15, Azure Application Gateway with no backend route left). The
// upstream protocol cannot be "fixed" — the owner decided to retire the provider entirely
// (Option B), following the phind/kluster quiet-removal precedent. This is the permanent
// regression guard: it asserts the provider is fully GONE from every runtime surface, not a
// live-network repro (which would be flaky/slow/depend on a third party we don't control).
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import assert from "node:assert/strict";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-13131-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const { hasSpecializedExecutor } = await import("../../open-sse/executors/index.ts");
const { REGISTRY } = await import("../../open-sse/config/providers/index.ts");
const { NOAUTH_PROVIDERS } = await import("../../src/shared/constants/providers/noauth.ts");
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("issue #13131: chipotle executor is no longer registered", () => {
assert.equal(hasSpecializedExecutor("chipotle"), false);
assert.equal(hasSpecializedExecutor("pepper"), false);
});
test("issue #13131: chipotle is no longer in the provider registry", () => {
assert.equal(Object.prototype.hasOwnProperty.call(REGISTRY, "chipotle"), false);
});
test("issue #13131: chipotle is no longer in the noauth provider catalog", () => {
assert.equal(Object.prototype.hasOwnProperty.call(NOAUTH_PROVIDERS, "chipotle"), false);
});
test("issue #13131: the chipotle executor module no longer exists", async () => {
await assert.rejects(() => import("../../open-sse/executors/chipotle.ts"));
});

View File

@@ -4,7 +4,7 @@
* our reference egress. As of this change that allowlist is narrowed to
* `opencode`: on the reference VPS (.15) it answers 200 with zero configuration.
* The other no-auth providers
* (duckduckgo-web, chipotle, aihorde) stay OUT of every auto/* pool
* (duckduckgo-web, aihorde) stay OUT of every auto/* pool
* until re-verified — they remain usable via direct `<alias>/<model>` calls, they
* are just not auto-routed to.
*
@@ -47,7 +47,7 @@ test.after(async () => {
});
const ALLOWED_NOAUTH_PROVIDERS = ["opencode"];
const EXCLUDED_NOAUTH_PROVIDERS = ["duckduckgo-web", "chipotle", "aihorde"];
const EXCLUDED_NOAUTH_PROVIDERS = ["duckduckgo-web", "aihorde"];
test("fresh install: the allowlisted no-auth providers are present in the auto-combo pool", async () => {
const combo = await virtualFactory.createVirtualAutoCombo(undefined);

View File

@@ -4,7 +4,7 @@
//
// Root cause: the custom-models loop in catalog.ts gated every model through
// hasEligibleConnectionForModel(getConnectionsForProvider(...)). noAuth providers
// (e.g. chipotle / alias "pepper") have NO DB connection rows, so getConnectionsForProvider
// (e.g. duckduckgo-web / alias "ddgw") have NO DB connection rows, so getConnectionsForProvider
// returns [] and hasEligibleConnectionForModel([]) === false → the model was dropped.
// Built-in models survived because they go through providerSupportsModel(), which has a
// noAuth bypass (#2798). This test asserts an IMPORTED model on a noAuth provider appears.
@@ -42,12 +42,12 @@ test.after(async () => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("#3200 imported model on a noAuth provider (chipotle) appears in /api/v1/models", async () => {
// chipotle is a noAuth provider (alias "pepper") — it never creates a DB connection row.
// Import a model that is NOT a built-in chipotle model, so its presence is solely due
test("#3200 imported model on a noAuth provider (duckduckgo-web) appears in /api/v1/models", async () => {
// duckduckgo-web is a noAuth provider (alias "ddgw") — it never creates a DB connection row.
// Import a model that is NOT a built-in duckduckgo-web model, so its presence is solely due
// to the custom/imported path (the path the bug breaks).
await modelsDb.addCustomModel(
"chipotle",
"duckduckgo-web",
"my-imported-model-3200",
"My Imported Model",
"imported"
@@ -61,7 +61,7 @@ test("#3200 imported model on a noAuth provider (chipotle) appears in /api/v1/mo
assert.equal(response.status, 200);
assert.ok(
ids.has("pepper/my-imported-model-3200"),
ids.has("ddgw/my-imported-model-3200"),
"imported model on noAuth provider must appear under its alias prefix"
);
});
@@ -89,9 +89,9 @@ test("#3200 custom/imported models on auth providers still appear (no regression
});
test("#3200 imported models on noAuth providers are hidden when the provider is disabled", async () => {
await settingsDb.updateSettings({ blockedProviders: ["chipotle"] });
await settingsDb.updateSettings({ blockedProviders: ["duckduckgo-web"] });
await modelsDb.addCustomModel(
"chipotle",
"duckduckgo-web",
"my-imported-model-disabled",
"Hidden Imported Model",
"imported"
@@ -105,7 +105,7 @@ test("#3200 imported models on noAuth providers are hidden when the provider is
assert.equal(response.status, 200);
assert.equal(
ids.has("pepper/my-imported-model-disabled"),
ids.has("ddgw/my-imported-model-disabled"),
false,
"imported noAuth provider models must stay hidden while the provider is disabled"
);

View File

@@ -1,6 +1,6 @@
/**
* Tests for noAuth provider validation:
* - Bug 1: `chipotle` missing from providerAllowsOptionalApiKey
* - Bug 1: a noAuth provider missing from providerAllowsOptionalApiKey
* - `kimi` API key provider stays on the dedicated Moonshot executor
*/
import test from "node:test";
@@ -14,7 +14,7 @@ import {
import { hasSpecializedExecutor } from "../../open-sse/executors/index.ts";
// Bug 1: all noAuth providers should allow optional API key
for (const provider of ["chipotle", "opencode", "duckduckgo-web", "veoaifree-web"]) {
for (const provider of ["cloudflare-playground", "opencode", "duckduckgo-web", "veoaifree-web"]) {
test(`${provider} allows optional API key (noAuth provider)`, () => {
assert.equal(providerAllowsOptionalApiKey(provider), true);
});

View File

@@ -17,7 +17,6 @@ const LOCAL_SVG_IDS_WITHOUT_PROVENANCE = [
"byteplus",
"cartesia",
"cheaperinference",
"chipotle",
"clarifai",
"command-code",
"digitalocean",
@@ -178,9 +177,9 @@ const AUDITED_REFERENCE_FILES = [
...referenceRoots.flatMap((directory) => collectTextFiles(join(root, directory))),
];
test("provider bundle retires exactly the 79 unresolved assets and keeps the generic icon", () => {
assert.equal(retiredAssetNames.length, 79);
assert.equal(new Set(retiredAssetNames).size, 79);
test("provider bundle retires exactly the 78 unresolved assets and keeps the generic icon", () => {
assert.equal(retiredAssetNames.length, 78);
assert.equal(new Set(retiredAssetNames).size, 78);
for (const assetName of retiredAssetNames) {
assert.equal(

View File

@@ -183,9 +183,13 @@ test("shared set size includes live REGISTRY and retired Designer + Felo + Qwen
// #13024 (2b9e7fb3e) GreenPT and #13025 (22473dee5) EURouter each add one REGISTRY member (id ==
// alias); #13277 (02128f334) registers Arcee AI, adding id "arcee-ai" + alias "arcee" (408 → 412).
// #13399 (cdcde97c7) registers Agnes AI (China): id "agnes-cn" + alias "agnescn" — the only
// two provider-level members added since; everything else in that range is model ids
// (412 -> 414). Same entry that moved the apikey/regional count to 241 in #13905.
assert.equal(RESERVED_PREFIX_COUNT, 414);
// two provider-level members added since (412 -> 414); everything else in that range is
// model ids. Same entry that moved the apikey/regional count to 241 in #13905.
// #13131 then retires `chipotle`/`pepper` (dead upstream), removing its id "chipotle" and
// alias "pepper" from the REGISTRY walk (414 -> 412) — the two land back on the same total.
// #12648 registers xKiro: id "xkiro" with no separate alias — a single REGISTRY
// member (412 -> 413).
assert.equal(RESERVED_PREFIX_COUNT, 413);
});
test("isReservedProviderPrefix rejects non-string input", () => {

View File

@@ -59,17 +59,17 @@ test("resolveProxyForConnection keeps provider-level no-auth proxies isolated",
host: "127.0.0.2",
port: 8889,
});
await settingsDb.setProxyForLevel("provider", "chipotle", {
await settingsDb.setProxyForLevel("provider", "duckduckgo-web", {
type: "http",
host: "127.0.0.3",
port: 8890,
});
const opencode = await settingsDb.resolveProxyForConnection("noauth", undefined, "opencode");
const chipotle = await settingsDb.resolveProxyForConnection("noauth", undefined, "chipotle");
const ddgw = await settingsDb.resolveProxyForConnection("noauth", undefined, "duckduckgo-web");
assert.equal(opencode?.proxy?.host, "127.0.0.2");
assert.equal(chipotle?.proxy?.host, "127.0.0.3");
assert.equal(ddgw?.proxy?.host, "127.0.0.3");
});
test("safeResolveProxy keeps the synthetic no-auth connection provider-specific", async () => {
@@ -79,15 +79,15 @@ test("safeResolveProxy keeps the synthetic no-auth connection provider-specific"
host: "127.0.0.4",
port: 8891,
});
await settingsDb.setProxyForLevel("provider", "chipotle", {
await settingsDb.setProxyForLevel("provider", "duckduckgo-web", {
type: "http",
host: "127.0.0.5",
port: 8892,
});
const opencode = await safeResolveProxy("noauth", undefined, "opencode");
const chipotle = await safeResolveProxy("noauth", undefined, "chipotle");
const ddgw = await safeResolveProxy("noauth", undefined, "duckduckgo-web");
assert.equal(opencode?.proxy?.host, "127.0.0.4");
assert.equal(chipotle?.proxy?.host, "127.0.0.5");
assert.equal(ddgw?.proxy?.host, "127.0.0.5");
});

View File

@@ -36,7 +36,6 @@ const PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE = [
"byteplus",
"cartesia",
"cheaperinference",
"chipotle",
"clarifai",
"command-code",
"digitalocean",
@@ -246,8 +245,8 @@ describe("ProviderIcon — local SVG dimensions", () => {
describe("ProviderIcon — unresolved local asset provenance", () => {
it("covers the complete provider and alias inventory", () => {
expect(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE).toHaveLength(79);
expect(new Set(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE)).toHaveLength(79);
expect(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE).toHaveLength(78);
expect(new Set(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE)).toHaveLength(78);
});
it.each(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE)(

View File

@@ -292,7 +292,7 @@ test("createVirtualAutoCombo restricts the no-auth pool to the allowlist", async
);
}
for (const excluded of ["duckduckgo-web", "chipotle", "aihorde"]) {
for (const excluded of ["duckduckgo-web", "aihorde"]) {
assert.equal(
combo.models.some((model) => model.providerId === excluded),
false,