Merge remote-tracking branch 'origin/release/v3.8.50' into fix/release-v3.8.50-basereds-cluster

This commit is contained in:
Xiangzhe
2026-08-23 16:13:55 -03:00
217 changed files with 12618 additions and 12910 deletions

View File

@@ -370,10 +370,31 @@ test("GET /api/settings/qdrant/embedding-models — returns models array", async
assert.strictEqual(res.status, 200);
const body = await res.json();
assert.ok(Array.isArray(body.models), "should have models array");
// Should have at least the default fallback model
assert.ok(body.models.length > 0, "should have at least one model");
assert.strictEqual(body.models.length, 0, "should not list models without a configured provider");
});
test("GET /api/settings/qdrant/embedding-models — lists only configured providers", async () => {
await localDb.createProviderConnection({
provider: "openai",
authType: "apikey",
name: "embedding-test-openai",
apiKey: "sk-test-embedding",
});
const headers = await createManagementSessionHeaders();
const req = new Request("http://localhost/api/settings/qdrant/embedding-models", {
method: "GET",
headers: Object.fromEntries(headers.entries()),
});
const res = await qdrantEmbeddingModelsRoute.GET(req as any);
assert.strictEqual(res.status, 200);
const body = await res.json();
assert.ok(body.models.length > 0, "should list models for configured provider");
assert.ok(body.models.every((model: any) => model.value.startsWith("openai/")));
assert.ok(body.models.some((model: any) => model.value === "openai/text-embedding-3-small"));
const defaultModel = body.models.find((m: any) => m.value === "openai/text-embedding-3-small");
assert.ok(defaultModel, "should include openai/text-embedding-3-small as default");
assert.match(defaultModel.label, /1536d/);
});
test("GET /api/settings/qdrant/embedding-models — 401 without auth", async () => {

View File

@@ -0,0 +1,238 @@
// #11244: visibility for the STRUCTURAL chat admission gate
// (src/shared/middleware/chatBodyAdmission.ts — the bounded heavyweight lease +
// healthy-headroom path from #10110/#10437, NOT the adaptive shadow-mode layer in
// open-sse/services/admission/). The 503 chat_admission_busy shed returns BEFORE
// request logging, so today a shed is invisible: no counter, no log line, and the
// process-wide snapshot (PerConnectionAdmissionController.snapshot()) reports only
// live state (activeHeavy/queuedBytes/waiting/lanes) with no shed history.
//
// These tests pin the observability contract WITHOUT changing admission behavior:
// (a) every structural shed (503 chat_admission_busy) increments an in-memory
// counter — total + per reason ("queue_timeout" when the bounded wait expires,
// "queued_bytes_budget" when the queued-bytes heap valve refuses to park) —
// while a client abort mid-wait is NOT a shed (capacity was never denied);
// (b) the process-wide snapshot exposes shedTotal + shedsByReason next to the
// existing live fields;
// (c) each shed emits exactly one structured pino warn carrying
// reason/activeHeavy/waiting and the HMAC session fingerprint — never the raw
// API key (resolveSessionId already fingerprints the credential).
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, existsSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
// Configure the shared pino logger BEFORE importing the admission module — the
// logger builds its transports at import time (see logger-redaction-wiring.test.ts
// for the same pattern). JSON to a temp file keeps test (c)'s capture deterministic.
const logDir = mkdtempSync(join(tmpdir(), "omniroute-admission-11244-"));
const logFile = join(logDir, "app.log");
process.env.NODE_ENV = "production";
process.env.APP_LOG_TO_FILE = "true";
process.env.APP_LOG_FILE_PATH = logFile;
const {
ChatAdmissionController,
PerConnectionAdmissionController,
perConnectionAdmissionController,
admitChatStructure,
resolveSessionId,
} = await import("../../src/shared/middleware/chatBodyAdmission.ts");
function heavyBody() {
return {
messages: Array.from({ length: 200 }, () => ({ role: "user", content: "x".repeat(40) })),
tools: [] as unknown[],
};
}
const heapHealthy = () => false; // "not under pressure" — the healthy-heap fast path
const heapPressured = () => true; // forces the bounded-wait/shed path deterministically
const silentSink = () => {}; // keep non-logging tests off the pino transport
test("#11244 (a): a structural shed after the bounded wait increments shedTotal and shedsByReason", async () => {
// Primary lease (1) + bounded healthy-headroom (1): two concurrent heavy requests
// admit on a healthy heap; the third must wait queueMs and then shed with a 503.
const controller = new ChatAdmissionController(1, undefined, 1, silentSink);
const first = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapHealthy,
queueMs: 0,
});
const second = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapHealthy,
queueMs: 0,
});
assert.equal(first.admit, true, "first heavy request takes the primary lease");
assert.equal(second.admit, true, "second heavy request takes the bounded headroom lease");
assert.equal(controller.shedTotal, 0, "admitted requests never count as sheds");
assert.deepEqual(controller.shedsByReason, {});
const shed = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapHealthy,
queueMs: 50,
});
assert.equal(shed.admit, false, "third heavy request must shed once both budgets are busy");
if (!shed.admit) {
assert.equal(shed.response.status, 503);
const payload = await shed.response.json();
assert.equal(payload.error.code, "chat_admission_busy");
}
assert.equal(
controller.shedTotal,
1,
"the shed must be counted even though it skips request logging"
);
assert.deepEqual(
controller.shedsByReason,
{ queue_timeout: 1 },
"a bounded wait that expires with no freed capacity is a queue_timeout shed"
);
// Counters are history, not live state: releasing the leases must not rewind them.
if (first.admit) first.lease?.release();
if (second.admit) second.lease?.release();
assert.equal(controller.shedTotal, 1, "shed history survives lease release");
// And a subsequently admitted request must not be counted.
const fourth = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapHealthy,
queueMs: 0,
});
assert.equal(fourth.admit, true);
assert.equal(controller.shedTotal, 1);
if (fourth.admit) fourth.lease?.release();
});
test("#11244 (a2): the queued-bytes heap valve rejection is counted with its own reason", async () => {
// maxQueuedBytes smaller than the conservative 256KB structural wait weight: the
// valve refuses to park and the shed must be distinguishable from a queue timeout.
const controller = new ChatAdmissionController(1, 1024, 0, silentSink);
const first = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapPressured,
queueMs: 0,
});
assert.equal(first.admit, true);
const shed = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapPressured,
queueMs: 1000,
});
assert.equal(shed.admit, false);
if (!shed.admit) assert.equal(shed.response.status, 503);
assert.equal(controller.shedTotal, 1);
assert.deepEqual(controller.shedsByReason, { queued_bytes_budget: 1 });
if (first.admit) first.lease?.release();
});
test("#11244 (a3): a client abort while parked is not a shed — capacity was never denied", async () => {
const controller = new ChatAdmissionController(1, undefined, 0, silentSink);
const first = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapPressured,
queueMs: 0,
});
assert.equal(first.admit, true);
const abort = new AbortController();
const pending = admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapPressured,
queueMs: 5000,
signal: abort.signal,
});
setTimeout(() => abort.abort(), 20);
const result = await pending;
assert.equal(
result.admit,
false,
"the caller still answers a (dropped) 503 on the dead connection"
);
assert.equal(controller.shedTotal, 0, "an aborted wait frees capacity instead of shedding");
assert.deepEqual(controller.shedsByReason, {});
if (first.admit) first.lease?.release();
});
test("#11244 (b): the process-wide snapshot exposes shed counters next to the live fields", async () => {
const empty = perConnectionAdmissionController.snapshot();
assert.equal(typeof empty.activeHeavy, "number");
assert.equal(typeof empty.queuedBytes, "number");
assert.equal(typeof empty.waiting, "number");
assert.ok(Array.isArray(empty.lanes));
assert.equal(
empty.shedTotal,
0,
"no shed happened through the production singleton in this process"
);
assert.deepEqual(empty.shedsByReason, {});
// A shed recorded through a session's controller surfaces in the aggregate snapshot.
const pc = new PerConnectionAdmissionController(1, { onShed: silentSink });
const controller = pc.getController("key_visibility11244");
controller.recordShed("queue_timeout", "key_visibility11244");
controller.recordShed("queue_timeout", "key_visibility11244");
controller.recordShed("queued_bytes_budget", "key_visibility11244");
const snap = pc.snapshot();
assert.equal(snap.shedTotal, 3);
assert.deepEqual(snap.shedsByReason, { queue_timeout: 2, queued_bytes_budget: 1 });
});
test("#11244 (c): each shed logs one structured warn with the session fingerprint, never the raw key", async () => {
const rawKey = "visRAWSECRETtoken11244xyz"; // matches no logRedaction pattern — a leak would show verbatim
const fingerprint = resolveSessionId(
new Request("http://localhost/v1/chat/completions", {
headers: { authorization: `Bearer ${rawKey}` },
})
);
assert.ok(fingerprint.startsWith("key_"), "resolveSessionId returns the HMAC fingerprint");
assert.ok(!fingerprint.includes(rawKey));
// Default sink (no injected onShed): the shed must go through the shared pino logger.
const controller = new ChatAdmissionController(1);
const primary = controller.tryAcquireHeavy();
assert.ok(primary);
const shed = await admitChatStructure(heavyBody(), null, {
controller,
heapPressureCheck: heapPressured,
queueMs: 25,
sessionId: fingerprint,
});
assert.equal(shed.admit, false);
primary.release();
// Poll the worker-thread-written log file until the shed line lands.
const deadline = Date.now() + 4000;
let contents = "";
while (Date.now() < deadline) {
if (existsSync(logFile)) {
contents = readFileSync(logFile, "utf8");
if (contents.includes("chat_admission_busy")) break;
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
assert.ok(contents.includes("chat_admission_busy"), "the shed log line names the rejection code");
assert.match(
contents,
/"level":(40|"warn")/,
"sheds log at warn level (numeric 40 when the file transport strips the level formatter)"
);
assert.ok(contents.includes('"module":"chat-admission"'), "the log is scoped to the gate");
assert.ok(contents.includes('"reason":"queue_timeout"'), "the shed reason is structured");
assert.ok(contents.includes('"activeHeavy":1'), "live state travels with the log line");
assert.ok(contents.includes(fingerprint), "the lane fingerprint allows per-key correlation");
assert.ok(!contents.includes(rawKey), "the raw API key must never reach the shed log");
});

View File

@@ -71,10 +71,13 @@ test("installed package contract requires sql.js metadata, entrypoint, and WASM"
[]
);
present.delete(path.join("/pkg", "dist/node_modules/sql.js/dist/sql-wasm.wasm"));
// Dependency-based packaging (#11242): sql.js is a declared dependency, so the
// contract path is the npm-installed <packageRoot>/node_modules/sql.js location,
// never the old vendored dist/node_modules one (banned from the tarball).
present.delete(path.join("/pkg", "node_modules/sql.js/dist/sql-wasm.wasm"));
assert.deepEqual(
findMissingSqlJsRuntimeFiles("/pkg", (file) => present.has(file)),
["dist/node_modules/sql.js/dist/sql-wasm.wasm"]
["node_modules/sql.js/dist/sql-wasm.wasm"]
);
});

View File

@@ -0,0 +1,131 @@
// #11226 — `omniroute auth export` crashed with "cmd.optsWithGlobals is not a
// function" because the command was registered as `.command("auth export")`:
// commander parses the bare word `export` as a REQUIRED POSITIONAL ARGUMENT, so
// the action received ("export", options, command) while its signature expected
// (options, command) — the classic opts/cmd swap. The fix registers `export` as
// a proper nested subcommand of `auth`, restoring the documented CLI surface
// (docs/reference/CLI-TOOLS.md): `omniroute auth export [--force] [--id] [--format] [--out]`.
//
// These tests exercise the REAL commander wiring via createProgram() — no DB is
// touched on any of these paths (the no-force gate prints and returns before any
// DB access; an invalid --format fails validation before opening the DB).
import test from "node:test";
import assert from "node:assert/strict";
import { createProgram } from "../../bin/cli/program.mjs";
function captureConsole(): { captured: { logs: string[]; errors: string[] }; restore: () => void } {
const originalLog = console.log;
const originalError = console.error;
const captured = { logs: [] as string[], errors: [] as string[] };
console.log = (msg?: unknown) => {
captured.logs.push(String(msg ?? ""));
};
console.error = (msg?: unknown) => {
captured.errors.push(String(msg ?? ""));
};
return {
captured,
restore: () => {
console.log = originalLog;
console.error = originalError;
},
};
}
function stubProcessExit(): { exitCodes: number[]; restore: () => void } {
const originalExit = process.exit;
const exitCodes: number[] = [];
process.exit = ((code?: number) => {
exitCodes.push(code ?? 0);
}) as typeof process.exit;
return {
exitCodes,
restore: () => {
process.exit = originalExit;
},
};
}
test("auth command exposes 'export' as a subcommand, not a positional argument", () => {
const program = createProgram();
const auth = program.commands.find((c) => c.name() === "auth");
assert.ok(auth, "auth command exists");
const exportCmd = auth.commands.find((c) => c.name() === "export");
assert.ok(exportCmd, "export must be a nested subcommand of auth");
const registeredArgs = (auth as unknown as { registeredArguments?: unknown[] })
.registeredArguments;
assert.equal(
registeredArgs?.length ?? 0,
0,
"auth must not declare positional arguments (a bare word in .command() becomes one)"
);
});
test("auth export action receives (options, command): flags reach the handler end-to-end", async () => {
const program = createProgram();
const exitStub = stubProcessExit();
const { captured, restore } = captureConsole();
try {
// --format bogus makes runAuthExportCommand return 1 BEFORE any DB access;
// the action must then call process.exit(1). With the opts/cmd swap this
// parse rejects with "cmd.optsWithGlobals is not a function" instead.
await program.parseAsync([
"node",
"omniroute",
"auth",
"export",
"--force",
"--format",
"bogus",
]);
} finally {
restore();
exitStub.restore();
}
assert.deepEqual(
exitStub.exitCodes,
[1],
"handler must receive --format and exit 1 on bogus value"
);
assert.ok(
captured.errors.join("\n").includes("Invalid format"),
`expected the invalid-format error, got: ${captured.errors.join(" | ")}`
);
});
test("auth export without --force prints the confirmation gate (no crash, no DB)", async () => {
const program = createProgram();
const exitStub = stubProcessExit();
const { captured, restore } = captureConsole();
try {
await program.parseAsync(["node", "omniroute", "auth", "export"]);
} finally {
restore();
exitStub.restore();
}
assert.deepEqual(exitStub.exitCodes, [], "dry run exits 0 without calling process.exit");
assert.ok(
captured.logs.join("\n").includes("DECRYPTED"),
`expected the confirmation gate, got: ${captured.logs.join(" | ")}`
);
});
test("auth rejects an unknown positional (was silently accepted as the 'export' argument)", async () => {
const program = createProgram();
await assert.rejects(
program.parseAsync(["node", "omniroute", "auth", "bogus-word"]),
(err: unknown) => {
assert.ok(err instanceof Error);
assert.match(
(err as { code?: string }).code || "",
/commander\.(unknownCommand|helpDisplayed)/
);
return true;
}
);
});

View File

@@ -27,7 +27,14 @@ async function withComboEnv(fn: (dataDir: string) => Promise<void>) {
} finally {
console.log = originalLog;
globalThis.fetch = ORIGINAL_FETCH;
fs.rmSync(dataDir, { recursive: true, force: true });
// On Windows the SQLite file may still be held open by the db module when
// the test ends, and rmSync then throws EPERM, failing a test whose
// assertions all passed. Retry, then give up quietly.
try {
fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
} catch {
// best effort: the OS reclaims its own temp dir
}
if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = ORIGINAL_DATA_DIR;

View File

@@ -108,13 +108,46 @@ test("runOAuthStatus consumes the connections envelope", async () => {
const parsed = JSON.parse(out);
assert.deepEqual(
parsed.map((connection: { id: string }) => connection.id),
["conn1", "conn2"],
["conn1", "conn2"]
);
} finally {
globalThis.fetch = origFetch;
}
});
test("runOAuthStatus tolerates an out-of-contract 200 payload (#11236)", async () => {
// Bug 5 residual: #10491 added the `data.connections ??` envelope, but a 200
// whose body is an object without connections/providers/items still fell
// through to `data` itself and crashed on `.filter is not a function`
// (followed by a libuv teardown assertion on Windows). The guard must coerce
// to an empty list and warn on stderr — never throw a raw TypeError.
const origFetch = globalThis.fetch;
// `as unknown as` (not `as any`): this file's no-explicit-any suppression is
// frozen at its pre-existing count, so new casts must be any-free.
globalThis.fetch = (() =>
Promise.resolve(makeResp({ status: "ok" }))) as unknown as typeof globalThis.fetch;
const stderrChunks: string[] = [];
const origStderr = process.stderr.write.bind(process.stderr);
process.stderr.write = ((chunk: string | Uint8Array) => {
if (typeof chunk === "string") stderrChunks.push(chunk);
return true;
}) as typeof process.stderr.write;
try {
const { runOAuthStatus } = await import("../../bin/cli/commands/oauth.mjs");
const out = await captureStdout(() => runOAuthStatus({}, makeCmd()));
assert.deepEqual(JSON.parse(out), []);
} finally {
globalThis.fetch = origFetch;
process.stderr.write = origStderr;
}
const warning = stderrChunks.join("");
assert.ok(warning.length > 0, "a sanitized warning must be written to stderr");
assert.ok(!warning.includes("at /"), "warning must not leak a stack trace");
});
test("runOAuthRevoke com --yes chama endpoint de revogação", async () => {
let capturedUrl = "";
let capturedMethod = "";

View File

@@ -0,0 +1,41 @@
/**
* Reverse the Windows `shell: true` argument escaping so assertions can be
* written against the logical argv on every platform.
*
* `bin/cli/commands/run.mjs` escapes argv before spawning, because on win32 the
* launchers must go through cmd.exe to run npm `.cmd` shims (CVE-2024-27980)
* and Node's `shell: true` joins argv with no escaping at all (DEP0190). That
* escaping is correct and deliberate, but it means `plan.args` holds
* `^^^"--model^^^"` on Windows where it holds `--model` elsewhere.
*
* Tests care about *which* arguments a plan carries, not about how they survive
* cmd.exe, so they normalise first. Keep this in sync with
* `escapeWindowsShellArg` in bin/cli/utils/winShellArgs.mjs.
*/
/**
* @param {unknown} arg
* @returns {string}
*/
export function unescapeWindowsShellArg(arg) {
let s = String(arg);
// 1. undo the two caret passes applied to cmd.exe metacharacters
s = s.replace(/\^(.)/g, "$1").replace(/\^(.)/g, "$1");
// 2. drop the wrapping quotes added by the CRT argv layer
if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) s = s.slice(1, -1);
// 3. undo the doubled backslashes and the escaped embedded quotes
s = s.replace(/\\\\/g, "\\").replace(/\\"/g, '"');
return s;
}
/**
* Normalise a plan's argv to its logical form. A no-op off Windows.
*
* @param {unknown[]} args
* @param {NodeJS.Platform|string} [platform]
* @returns {string[]}
*/
export function logicalArgs(args, platform = process.platform) {
const list = [...(args ?? [])].map(String);
return platform === "win32" ? list.map(unescapeWindowsShellArg) : list;
}

View File

@@ -18,7 +18,7 @@ import { spawnSync } from "node:child_process";
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
resolveAlias,
@@ -30,6 +30,13 @@ import {
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const REPO_ROOT = join(__dirname, "..", "..", "..");
// The child scripts below `import()` these paths, and `import()` resolves its
// specifier as a URL. Replacing backslashes with forward slashes is not enough
// on Windows: the leading drive letter is then parsed as the URL scheme `e:`,
// which the ESM loader rejects with ERR_UNSUPPORTED_ESM_URL_SCHEME. Emit a
// real file:// URL instead.
const repoFileUrl = (relPath) => pathToFileURL(join(REPO_ROOT, relPath)).href;
describe("aliasResolver.resolveAlias (pure)", () => {
it("returns null for non-@/ specifiers (lets Node/tsx handle them)", () => {
assert.equal(resolveAlias("node:fs", REPO_ROOT), null);
@@ -260,11 +267,11 @@ describe("aliasResolver end-to-end (#7791 regression)", () => {
const script = `
await import("tsx/esm");
import { join } from "node:path";
import { registerAliasResolver } from "${join(REPO_ROOT, "bin/aliasResolver.mjs").replace(/\\/g, "/")}";
import { registerAliasResolver } from ${JSON.stringify(repoFileUrl("bin/aliasResolver.mjs"))};
const ok = await registerAliasResolver(${JSON.stringify(REPO_ROOT)});
if (!ok) { console.error("FAIL: registerAliasResolver returned false"); process.exit(2); }
try {
const m = await import(${JSON.stringify(join(REPO_ROOT, "src/shared/network/outboundUrlGuard.ts").replace(/\\/g, "/"))});
const m = await import(${JSON.stringify(repoFileUrl("src/shared/network/outboundUrlGuard.ts"))});
const keys = Object.keys(m).sort().join(",");
console.log("OK:" + keys);
} catch (err) {
@@ -286,7 +293,7 @@ describe("aliasResolver end-to-end (#7791 regression)", () => {
it("does not interfere with bare/relative specifiers (regression guard)", () => {
const script = `
import { registerAliasResolver } from "${join(REPO_ROOT, "bin/aliasResolver.mjs").replace(/\\/g, "/")}";
import { registerAliasResolver } from ${JSON.stringify(repoFileUrl("bin/aliasResolver.mjs"))};
await registerAliasResolver(${JSON.stringify(REPO_ROOT)});
// node:fs must still resolve via the default resolver
const fs = await import("node:fs");

View File

@@ -7,6 +7,7 @@ import {
resolveModelFromTargetOptions,
runCliTarget,
} from "../../../bin/cli/commands/run.mjs";
import { logicalArgs } from "./_helpers/shellArgs.mjs";
test("resolveRunTarget resolves aliases", () => {
assert.equal(resolveRunTarget("claude"), "claude");
@@ -39,7 +40,7 @@ test("buildRunPlan for claude includes env diff and model injection", async () =
assert.equal(plan.target, "claude");
assert.equal(plan.baseUrl, "http://localhost:20128");
assert.equal(plan.model, "gpt-5");
assert.equal(plan.args.includes("--help"), true);
assert.equal(logicalArgs(plan.args).includes("--help"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("ANTHROPIC_AUTH_TOKEN"), true);
assert.equal(plan.authSource, "option");
assert.equal(plan.command.includes("claude"), true);
@@ -54,9 +55,9 @@ test("buildRunPlan for codex injects model into provider args", async () => {
assert.equal(plan.target, "codex");
assert.equal(plan.baseUrl, "http://localhost:20128");
assert.equal(plan.model, "glm/glm-4.5");
assert.equal(plan.args.includes("--help"), true);
assert.equal(logicalArgs(plan.args).includes("--help"), true);
assert.equal(
plan.args.some((a) => String(a).includes("model_providers.omniroute.model")),
logicalArgs(plan.args).some((a) => a.includes("model_providers.omniroute.model")),
true
);
assert.equal(plan.authSource, "option");
@@ -70,7 +71,7 @@ test("buildRunPlan for Aider uses its OpenAI-compatible root endpoint", async ()
);
assert.equal(plan.target, "aider");
assert.equal(plan.baseUrl, "https://relay.example.test");
assert.deepEqual(plan.args.slice(0, 2), ["--model", "openai/glm/glm-5.2"]);
assert.deepEqual(logicalArgs(plan.args).slice(0, 2), ["--model", "openai/glm/glm-5.2"]);
assert.equal(plan.envDiff.changedOrAdded.includes("OPENAI_API_BASE"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("OPENAI_API_KEY"), true);
});
@@ -82,7 +83,7 @@ test("buildRunPlan for Goose injects provider and model without writing config",
["session"]
);
assert.equal(plan.target, "goose");
assert.deepEqual(plan.args, ["session"]);
assert.deepEqual(logicalArgs(plan.args), ["session"]);
assert.equal(plan.envDiff.changedOrAdded.includes("GOOSE_PROVIDER"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("GOOSE_MODEL"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("OPENAI_HOST"), true);
@@ -95,7 +96,7 @@ test("buildRunPlan for OpenCode uses an ephemeral compatible config", async () =
["run", "reply OK"]
);
assert.equal(plan.target, "opencode");
assert.deepEqual(plan.args.slice(0, 2), ["--model", "omniroute/glm/glm-5.2"]);
assert.deepEqual(logicalArgs(plan.args).slice(0, 2), ["--model", "omniroute/glm/glm-5.2"]);
assert.equal(plan.envDiff.changedOrAdded.includes("OPENCODE_CONFIG_CONTENT"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("OMNIROUTE_API_KEY"), true);
assert.equal(plan.configOverlay, "OPENCODE_CONFIG_CONTENT (process environment only)");
@@ -109,7 +110,7 @@ test("buildRunPlan for Qwen requires a deterministic model and injects only env
["-p", "reply OK"]
);
assert.equal(plan.target, "qwen");
assert.deepEqual(plan.args.slice(0, 2), ["--model", "glm/glm-5.2"]);
assert.deepEqual(logicalArgs(plan.args).slice(0, 2), ["--model", "glm/glm-5.2"]);
assert.equal(plan.envDiff.changedOrAdded.includes("OMNIROUTE_API_KEY"), true);
assert.equal(plan.configOverlay, "temporary QWEN_HOME (removed after exit)");
await assert.rejects(
@@ -126,7 +127,7 @@ test("buildRunPlan for Gemini points the CLI at the /v1beta surface via env", as
);
assert.equal(plan.target, "gemini");
assert.equal(plan.baseUrl, "https://relay.example.test");
assert.deepEqual(plan.args.slice(0, 2), ["--model", "glm/glm-5.2"]);
assert.deepEqual(logicalArgs(plan.args).slice(0, 2), ["--model", "glm/glm-5.2"]);
assert.equal(plan.envDiff.changedOrAdded.includes("GOOGLE_GEMINI_BASE_URL"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("GEMINI_API_KEY"), true);
assert.equal(plan.envDiff.changedOrAdded.includes("GEMINI_DEFAULT_AUTH_TYPE"), true);

View File

@@ -0,0 +1,68 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
// Regression guard for the Windows-only ESM loader failure:
//
// Error: Only URLs with a scheme in: file, data, and node are supported by
// the default ESM loader. On Windows, absolute paths must be valid file://
// URLs. Received protocol 'e:'
//
// `import()` resolves its specifier as a URL. A POSIX absolute path like
// /home/x/src/lib/db/combos.ts happens to also be a valid relative URL, so
// interpolating it works by accident. A Windows absolute path is
// E:\checkout\src\lib\db\combos.ts, whose leading drive letter the loader
// parses as the URL scheme `e:` and rejects. Every such call site must go
// through pathToFileURL().
//
// This broke `omniroute combo list/create/delete/switch` on Windows whenever
// the CLI fell back to direct DB access with the server offline.
const CLI_DIR = path.join(PROJECT_ROOT, "bin", "cli");
function collectMjsFiles(dir: string): string[] {
const out: string[] = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) out.push(...collectMjsFiles(full));
else if (entry.name.endsWith(".mjs")) out.push(full);
}
return out;
}
test("bin/cli never passes an interpolated absolute path to dynamic import()", () => {
// Matches import(`${ANY_ROOT_CONST}/...`) — a raw filesystem path, not a URL.
const badImport = /\bimport\(\s*`\$\{[A-Za-z_$][\w$]*\}\//;
const offenders: string[] = [];
for (const file of collectMjsFiles(CLI_DIR)) {
const source = fs.readFileSync(file, "utf8");
source.split(/\r?\n/).forEach((line, i) => {
if (badImport.test(line)) {
offenders.push(`${path.relative(PROJECT_ROOT, file)}:${i + 1}: ${line.trim()}`);
}
});
}
assert.deepEqual(
offenders,
[],
"dynamic import() of an interpolated absolute path fails on Windows; " +
`wrap the path in pathToFileURL(...).href instead:\n${offenders.join("\n")}`,
);
});
test("runtime.mjs resolves db modules to a file:// URL", async () => {
const source = fs.readFileSync(path.join(CLI_DIR, "runtime.mjs"), "utf8");
assert.match(source, /pathToFileURL/, "runtime.mjs must build file:// URLs for dynamic imports");
// The real proof: the db fallback modules actually load on this platform.
const runtime = await import(pathToFileURL(path.join(CLI_DIR, "runtime.mjs")).href);
const ctx = await runtime.withDb(async (c: { kind: string; db: unknown }) => c);
assert.equal(ctx.kind, "db");
assert.ok(ctx.db);
});

View File

@@ -22,3 +22,13 @@ test("valid input yields no rejected keys", () => {
assert.deepEqual(r.rejected, []);
assert.deepEqual(r.sanitized, { rpm: 10, tpm: 20 });
});
// #11251 added `maxWaitMs` to the Zod validation schema and the
// EditConnectionModal UI, but not to this separate allowlist — saving the
// field from the dashboard threw "Refusing to persist rateLimitOverrides
// with rejected keys: maxWaitMs" (500) on every attempt.
test("sanitizeRateLimitOverrides accepts maxWaitMs (#11251 follow-up)", () => {
const r = sanitizeRateLimitOverrides({ minTime: 500, maxWaitMs: 30000 });
assert.deepEqual(r.rejected, []);
assert.deepEqual(r.sanitized, { minTime: 500, maxWaitMs: 30000 });
});

View File

@@ -0,0 +1,83 @@
/**
* tests/unit/combo-max-global-attempts-config.test.ts
*
* Issue #11134: the shared per-request combo attempt budget was the hardcoded
* `MAX_GLOBAL_ATTEMPTS = 30` in comboPredicates.ts, with no env/config override
* (confirmed by the repo owner on the issue). Operators running large combos
* (or wanting to fail fast on a dead pool) could neither raise nor lower it.
*
* This mirrors the established `clampComboDepth` pattern exactly: an operator
* knob (`config.maxGlobalAttempts`) that can raise the default (30) or lower it,
* but never above `MAX_GLOBAL_ATTEMPTS_HARD_CAP` — an unbounded attempt budget
* is the same runaway-request DoS risk that motivated MAX_COMBO_DEPTH_HARD_CAP.
*/
import test from "node:test";
import assert from "node:assert/strict";
test("clampGlobalAttempts — clamps to [1, hard cap]; invalid → default 30", async () => {
const { clampGlobalAttempts, MAX_GLOBAL_ATTEMPTS, MAX_GLOBAL_ATTEMPTS_HARD_CAP } =
await import("../../open-sse/services/combo.ts");
assert.equal(MAX_GLOBAL_ATTEMPTS, 30, "default budget unchanged");
assert.equal(MAX_GLOBAL_ATTEMPTS_HARD_CAP, 200, "absolute safety ceiling");
// Honors a LOWER configured budget (fail fast on a dead pool — the #11134 symptom).
assert.equal(clampGlobalAttempts(1), 1);
assert.equal(clampGlobalAttempts(5), 5);
// Honors a HIGHER configured budget (large combos legitimately need more).
assert.equal(clampGlobalAttempts(120), 120);
// …but never past the hard cap.
assert.equal(clampGlobalAttempts(10_000), 200, "hard cap at 200");
// Invalid values fall back to the default, never disabling the budget.
assert.equal(clampGlobalAttempts(0), 30, "0 invalid → default 30");
assert.equal(clampGlobalAttempts(-4), 30, "negative → default 30");
assert.equal(clampGlobalAttempts(undefined), 30, "undefined → default 30");
assert.equal(clampGlobalAttempts("abc"), 30, "non-numeric → default 30");
assert.equal(clampGlobalAttempts(Number.NaN), 30, "NaN → default 30");
assert.equal(clampGlobalAttempts(Infinity), 30, "Infinity → default 30 (never unbounded)");
assert.equal(clampGlobalAttempts(4.9), 4, "floors to 4");
});
test("DEFAULT_COMBO_CONFIG — exposes maxGlobalAttempts so the cascade can override it", async () => {
const { getDefaultComboConfig, resolveComboConfig } =
await import("../../open-sse/services/comboConfig.ts");
assert.equal(getDefaultComboConfig().maxGlobalAttempts, 30, "default present in config surface");
// Per-combo config wins over the global default (standard cascade).
const resolved = resolveComboConfig({ config: { maxGlobalAttempts: 7 } }, {});
assert.equal(resolved.maxGlobalAttempts, 7);
// settings.comboDefaults layer also applies.
const fromGlobal = resolveComboConfig({}, { comboDefaults: { maxGlobalAttempts: 50 } });
assert.equal(fromGlobal.maxGlobalAttempts, 50);
});
test("dispatchPrelude — configured budget reaches nesting.attemptBudget.limit", async () => {
const { buildDefaultNesting } = await import("../../open-sse/services/combo/dispatchPrelude.ts");
// buildDefaultNesting only reads maxComboDepth/maxGlobalAttempts off config;
// the full resolved-config type is irrelevant to this assertion.
const build = (cfg: Record<string, unknown>) =>
(
buildDefaultNesting as (
n: null,
name: string,
c: unknown
) => { attemptBudget: { limit: number } }
)(null, "c", cfg);
// Unset → historical default of 30.
assert.equal(build({}).attemptBudget.limit, 30);
// Configured lower → honored (fail fast).
assert.equal(build({ maxGlobalAttempts: 6 }).attemptBudget.limit, 6);
// Configured higher → honored.
assert.equal(build({ maxGlobalAttempts: 90 }).attemptBudget.limit, 90);
// Absurd → hard-capped, never unbounded.
assert.equal(build({ maxGlobalAttempts: 1e9 }).attemptBudget.limit, 200);
});
test("combo schema — accepts maxGlobalAttempts within the hard cap, rejects beyond", async () => {
const { comboRuntimeConfigSchema: schema } =
await import("../../src/shared/validation/schemas/combo.ts");
assert.equal(schema.parse({ maxGlobalAttempts: 45 }).maxGlobalAttempts, 45);
assert.equal(schema.safeParse({ maxGlobalAttempts: 201 }).success, false, "beyond hard cap");
assert.equal(schema.safeParse({ maxGlobalAttempts: 0 }).success, false, "0 rejected");
});

View File

@@ -0,0 +1,164 @@
// @vitest-environment jsdom
import React, { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
vi.mock("next-intl", () => ({
useTranslations: () => (key: string) => key,
}));
vi.mock("@/store/notificationStore", () => ({
useNotificationStore: () => ({ notify: vi.fn() }),
}));
vi.mock("@/store/emailPrivacyStore", () => ({
default: () => ({ hidden: false, toggle: vi.fn() }),
}));
// Expanding "Advanced settings" also mounts ProviderTierField (#7818), which
// fetches its current tier override on mount independent of this modal's own
// save flow. Mock it out — its network call is unrelated to maxWaitMs.
vi.mock(
"@/app/(dashboard)/dashboard/providers/[id]/components/modals/providerTierFieldApi",
() => ({
fetchProviderTierOverride: vi.fn().mockResolvedValue(""),
saveProviderTierOverride: vi.fn().mockResolvedValue(undefined),
})
);
const { default: EditConnectionModal } =
await import("../../../src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx");
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => root.unmount());
container.remove();
vi.clearAllMocks();
});
function renderModal(connection: Record<string, unknown>) {
act(() => {
root.render(
<EditConnectionModal
isOpen={true}
connection={connection}
providerId={connection.provider as string}
onSave={vi.fn().mockResolvedValue(undefined)}
onClose={vi.fn()}
/>
);
});
}
function expandAdvancedSettings() {
// The "Rate Limit Overrides" section (like the rest of the advanced fields)
// is collapsed by default behind the "Advanced settings" disclosure toggle.
const toggle = container.querySelector(
'button[aria-controls="edit-connection-advanced-settings"]'
) as HTMLButtonElement | null;
expect(toggle).not.toBeNull();
act(() => {
toggle!.click();
});
}
function findMaxWaitMsInput(): HTMLInputElement | null {
const label = Array.from(container.querySelectorAll("label")).find(
(el) => el.textContent === "rateLimitOverridesMaxWaitMsLabel"
);
const forId = label?.getAttribute("for");
return forId ? (container.querySelector(`#${forId}`) as HTMLInputElement | null) : null;
}
function clickSave() {
const button = Array.from(container.querySelectorAll("button")).find(
(b) => b.textContent === "save"
);
expect(button).toBeTruthy();
button!.click();
}
describe("EditConnectionModal — maxWaitMs rate-limit override", () => {
it("renders an empty maxWaitMs field for a connection with no override", () => {
renderModal({
id: "conn-1",
provider: "nvidia",
authType: "apikey",
name: "key",
rateLimitOverrides: { rpm: 30 },
});
expandAdvancedSettings();
const input = findMaxWaitMsInput();
expect(input).not.toBeNull();
expect(input?.value).toBe("");
});
it("preserves a persisted maxWaitMs override in form state", () => {
renderModal({
id: "conn-2",
provider: "nvidia",
authType: "apikey",
name: "key",
rateLimitOverrides: { maxWaitMs: 45000 },
});
expandAdvancedSettings();
const input = findMaxWaitMsInput();
expect(input?.value).toBe("45000");
});
it("submits the entered maxWaitMs as rateLimitOverrides.maxWaitMs", async () => {
// The "Rate Limit Overrides" section only renders for non-OAuth
// connections (`{!isOAuth && (...)}` wraps it, same gate as
// rpm/minTime/maxConcurrent). formData.apiKey stays "" (untouched by this
// test), so handleSubmit's `!isOAuth && formData.apiKey` validation-fetch
// branch is skipped and the save completes synchronously without mocking
// `fetch`.
const onSave = vi.fn().mockResolvedValue(undefined);
act(() => {
root.render(
<EditConnectionModal
isOpen={true}
connection={{
id: "conn-3",
provider: "nvidia",
authType: "apikey",
name: "key",
}}
providerId="nvidia"
onSave={onSave}
onClose={vi.fn()}
/>
);
});
expandAdvancedSettings();
const input = findMaxWaitMsInput();
expect(input).not.toBeNull();
const setter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
)!.set!;
await act(async () => {
setter.call(input, "45000");
input!.dispatchEvent(new Event("input", { bubbles: true }));
});
await act(async () => {
clickSave();
});
expect(onSave).toHaveBeenCalledTimes(1);
const updates = onSave.mock.calls[0][0] as {
rateLimitOverrides: Record<string, number> | null;
};
expect(updates.rateLimitOverrides?.maxWaitMs).toBe(45000);
});
});

View File

@@ -0,0 +1,112 @@
/**
* effort_tiers loop — I1 end-to-end proof: a set recorded through the REAL
* record path (executor-style connection key) surfaces in the REAL catalog
* response (/api/v1/models), including the learned-only variant entry.
* Never "fix" this test by injecting the same string on both sides.
*/
import { test, after, beforeEach } 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-effort-loop-e2e-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "loop-e2e-secret";
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const v1ModelsCatalog = await import("../../src/app/api/v1/models/catalog.ts");
const { recordLearnedReasoningEffort, __test_resetLearnedReasoningEffortCaps } =
await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
// Copied verbatim from sync-reasoning-supported-efforts-7694.test.ts
async function seedProviderConnection(provider: string) {
return providersDb.createProviderConnection({
provider,
authType: "apikey",
name: `${provider}-${Math.random().toString(16).slice(2, 8)}`,
apiKey: `${provider}-key`,
isActive: true,
testStatus: "active",
});
}
test.beforeEach(async () => {
__test_resetLearnedReasoningEffortCaps();
await resetStorage();
});
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("learned set flows end-to-end into /v1/models capabilities and variant entries", async () => {
// Non-namespaced id on purpose: mirrors the real incident model
// (x-preview-f-free) where sm.id === the executor-visible post-strip id.
const MODEL_ID = "loop-model-e2e";
const connection = await seedProviderConnection("huggingface");
await modelsDb.replaceSyncedAvailableModelsForConnection("huggingface", connection.id, [
{
id: MODEL_ID,
name: "Loop Model E2E",
supportedThinkingEfforts: ["none", "low", "medium", "high"],
},
]);
// Simulate the real 400 learning path (base.ts calls exactly this, with the
// executor's CONNECTION id as provider key):
recordLearnedReasoningEffort("openai-compatible-chat-eaff6869", MODEL_ID, ["low", "high", "max"]);
const response = await v1ModelsCatalog.getUnifiedModelsResponse(
new Request("http://localhost/api/v1/models")
);
assert.equal(response.status, 200);
const body = (await response.json()) as {
data: Array<{ id: string; capabilities?: { effort_tiers?: string[] } }>;
};
const baseEntry = body.data.find((m) => m.id.endsWith(MODEL_ID));
assert.ok(baseEntry, "base entry present");
assert.deepEqual(baseEntry!.capabilities?.effort_tiers, ["low", "high", "max"]);
const maxVariant = body.data.find((m) => m.id === `${baseEntry!.id}-max`);
assert.ok(maxVariant, "learned-only tier synthesized as a variant entry");
});
test("excluded provider (glm) never surfaces effort_tiers, learned or synced", async () => {
const MODEL_ID = "glm-4-flash";
const connection = await seedProviderConnection("glm");
await modelsDb.replaceSyncedAvailableModelsForConnection("glm", connection.id, [
{
id: MODEL_ID,
name: "GLM 4 Flash",
supportedThinkingEfforts: ["none", "low", "medium", "high"],
},
]);
recordLearnedReasoningEffort("glm-connection-1", MODEL_ID, ["low", "high"]);
const response = await v1ModelsCatalog.getUnifiedModelsResponse(
new Request("http://localhost/api/v1/models")
);
assert.equal(response.status, 200);
const body = (await response.json()) as {
data: Array<{ id: string; capabilities?: { effort_tiers?: string[] } }>;
};
const baseEntry = body.data.find((m) => m.id.endsWith(MODEL_ID));
assert.ok(baseEntry, "base entry present");
assert.equal(
baseEntry!.capabilities?.effort_tiers,
undefined,
"glm owns its own -{effort} suffix mechanism — the catalog must not also expose effort_tiers"
);
});

View File

@@ -10,7 +10,7 @@
* GET https://g4f.space/api/groq/... → live Groq backend
*
* Verifies each of the 5 sub-path providers is wired end-to-end the same way as
* the other no-key gateway providers (hackclub, uncloseai):
* the other no-key gateway providers (uncloseai):
* - present in the executor REGISTRY with a no-key OpenAI-compatible shape
* - resolvable through getExecutor() (falls through to DefaultExecutor)
* - listed in AGGREGATOR_PROVIDER_IDS so it shows up in the aggregator
@@ -84,7 +84,7 @@ for (const [id, subPath] of Object.entries(SUB_PATHS)) {
test(`#6650 ${id} is classified as an aggregator/gateway provider`, () => {
assert.ok(
AGGREGATOR_PROVIDER_IDS.has(id),
`${id} must be listed in AGGREGATOR_PROVIDER_IDS alongside hackclub/uncloseai`
`${id} must be listed in AGGREGATOR_PROVIDER_IDS alongside uncloseai`
);
});

View File

@@ -0,0 +1,139 @@
/**
* #11176 — Hack Club AI (hackclub) must be FULLY removed from the live catalogs.
*
* PR #11118/#11123 removed the provider from the open-sse REGISTRY, but the
* canonical shared catalog (`src/shared/constants/providers/`) kept the entry,
* so the dashboard, the alias resolver, the icon set, the onboarding i18n
* strings and the generated provider reference kept advertising a provider the
* router can no longer serve. This test pins the complete removal:
*
* 1. no canonical catalog (API-key / web-cookie / OAuth / no-auth / local /
* search / audio / upstream-proxy / cloud-agent / system) has a `hackclub` entry;
* 2. no provider in any catalog claims the `hc` alias (it belonged to hackclub);
* 3. the provider/catalog source trees carry no `hackclub` mention at all
* (structural grep — catches comments referencing it as a living provider);
* 4. the icon registry and the shipped SVG asset are gone;
* 5. the onboarding i18n description key is gone (en + all locale mirrors).
*
* Historical mentions intentionally KEPT (release records, not catalog):
* CHANGELOG.md, docs/i18n/*\/CHANGELOG.md, and the removal migration
* src/lib/db/migrations/162_remove_hackclub_provider.sql (it IS the removal).
*/
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
APIKEY_PROVIDERS,
WEB_COOKIE_PROVIDERS,
OAUTH_PROVIDERS,
FREE_PROVIDERS,
NOAUTH_PROVIDERS,
LOCAL_PROVIDERS,
SEARCH_PROVIDERS,
AUDIO_ONLY_PROVIDERS,
UPSTREAM_PROXY_PROVIDERS,
CLOUD_AGENT_PROVIDERS,
SYSTEM_PROVIDERS,
} from "../../src/shared/constants/providers.ts";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
const CATALOGS: Record<string, Record<string, { id?: string; alias?: string }>> = {
APIKEY_PROVIDERS,
WEB_COOKIE_PROVIDERS,
OAUTH_PROVIDERS: OAUTH_PROVIDERS as Record<string, { id?: string; alias?: string }>,
FREE_PROVIDERS: FREE_PROVIDERS as Record<string, { id?: string; alias?: string }>,
NOAUTH_PROVIDERS: NOAUTH_PROVIDERS as Record<string, { id?: string; alias?: string }>,
LOCAL_PROVIDERS: LOCAL_PROVIDERS as Record<string, { id?: string; alias?: string }>,
SEARCH_PROVIDERS: SEARCH_PROVIDERS as Record<string, { id?: string; alias?: string }>,
AUDIO_ONLY_PROVIDERS: AUDIO_ONLY_PROVIDERS as Record<string, { id?: string; alias?: string }>,
UPSTREAM_PROXY_PROVIDERS: UPSTREAM_PROXY_PROVIDERS as Record<string, { id?: string; alias?: string }>,
CLOUD_AGENT_PROVIDERS: CLOUD_AGENT_PROVIDERS as Record<string, { id?: string; alias?: string }>,
SYSTEM_PROVIDERS: SYSTEM_PROVIDERS as Record<string, { id?: string; alias?: string }>,
};
function walk(dir: string): string[] {
const out: string[] = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) out.push(...walk(full));
else if (/\.(ts|tsx|mts|json)$/.test(entry.name)) out.push(full);
}
return out;
}
test("hackclub is absent from every canonical provider catalog", () => {
for (const [name, catalog] of Object.entries(CATALOGS)) {
assert.equal(
"hackclub" in catalog,
false,
`${name} still contains a hackclub entry (#11176)`
);
}
});
test("no provider in any catalog claims the `hc` alias (it belonged to hackclub)", () => {
const holders: string[] = [];
for (const [name, catalog] of Object.entries(CATALOGS)) {
for (const [key, p] of Object.entries(catalog)) {
if (p?.alias === "hc" || key === "hc") holders.push(`${name}:${key}`);
}
}
assert.deepEqual(holders, [], `alias "hc" still claimed by: ${holders.join(", ")}`);
});
test("no hackclub mention survives in the provider/catalog source trees", () => {
const scopedDirs = [
path.join(ROOT, "src", "shared", "constants", "providers"),
path.join(ROOT, "open-sse", "config"),
];
const offenders: string[] = [];
for (const dir of scopedDirs) {
for (const file of walk(dir)) {
if (/hack\s*club|hackclub/i.test(fs.readFileSync(file, "utf8"))) {
offenders.push(path.relative(ROOT, file));
}
}
}
assert.deepEqual(
offenders,
[],
`hackclub mentions left in catalog sources: ${offenders.join(", ")}`
);
});
test("hackclub icon registration and shipped SVG asset are gone", () => {
const iconSource = fs.readFileSync(
path.join(ROOT, "src", "shared", "components", "ProviderIcon.tsx"),
"utf8"
);
assert.equal(
/hackclub/i.test(iconSource),
false,
"ProviderIcon.tsx still registers hackclub (#11176)"
);
assert.equal(
fs.existsSync(path.join(ROOT, "public", "providers", "hackclub.svg")),
false,
"public/providers/hackclub.svg still shipped (#11176)"
);
});
test("onboarding i18n description for hackclub is gone from every locale", () => {
const messagesDir = path.join(ROOT, "src", "i18n", "messages");
const offenders: string[] = [];
for (const file of fs.readdirSync(messagesDir)) {
if (!file.endsWith(".json")) continue;
const messages = JSON.parse(fs.readFileSync(path.join(messagesDir, file), "utf8"));
const descriptions = messages?.providers?.onboardingProviderDescriptions;
if (descriptions && "hackclub" in descriptions) offenders.push(file);
}
assert.deepEqual(
offenders,
[],
`onboardingProviderDescriptions.hackclub still present in: ${offenders.join(", ")}`
);
});

View File

@@ -18,7 +18,7 @@ after(() => {
// ── REASONING_EFFORT_ORDER ──────────────────────────────────────────────────
test("REASONING_EFFORT_ORDER is none < minimal < low < medium < high < xhigh < max", () => {
test("REASONING_EFFORT_ORDER is none < minimal < low < medium < high < xhigh < max < ultra", () => {
assert.deepEqual(REASONING_EFFORT_ORDER, [
"none",
"minimal",
@@ -27,6 +27,24 @@ test("REASONING_EFFORT_ORDER is none < minimal < low < medium < high < xhigh < m
"high",
"xhigh",
"max",
"ultra",
]);
});
test("REASONING_EFFORT_ORDER ends with ultra", () => {
assert.equal(REASONING_EFFORT_ORDER.at(-1), "ultra");
});
test("parseReasoningEffortEnum extracts please use low, high, or max", () => {
const err =
"This model always engages in thinking and cannot be disabled; please use low, high, or max";
assert.deepEqual(parseReasoningEffortEnum(err), ["low", "high", "max"]);
});
test("parseReasoningEffortEnum extracts please use with ultra", () => {
assert.deepEqual(parseReasoningEffortEnum("please use low, high, max, ultra"), [
"low",
"high",
"max",
"ultra",
]);
});
@@ -70,9 +88,15 @@ test("records the highest recognized value from the accepted list", () => {
"medium",
"low",
"minimal",
]);
assert.equal(learned, "high");
assert.equal(getLearnedReasoningEffort("ovh", "qwen3-coder-30b-a3b-instruct"), "high");
]) as unknown as Set<string>;
assert.ok(learned instanceof Set);
assert.ok(learned.has("high"));
assert.equal(
(
getLearnedReasoningEffort("ovh", "qwen3-coder-30b-a3b-instruct") as unknown as Set<string>
).has("high"),
true
);
});
test("returns null and stores nothing when acceptedValues has no recognized token", () => {
@@ -89,26 +113,84 @@ test("monotonic decrease: a later, higher accepted-list never ratchets the cap b
"medium",
"high",
"xhigh",
]);
assert.equal(learned, "medium");
assert.equal(getLearnedReasoningEffort("acme", "model-x"), "medium");
]) as unknown as Set<string>;
assert.equal(learned.size, 3);
assert.ok(learned.has("medium"));
assert.equal((getLearnedReasoningEffort("acme", "model-x") as unknown as Set<string>).size, 3);
});
test("a later, lower accepted-list does ratchet the cap down", () => {
recordLearnedReasoningEffort("acme", "model-x", ["none", "low", "medium", "high"]);
const learned = recordLearnedReasoningEffort("acme", "model-x", ["none", "low"]);
assert.equal(learned, "low");
assert.equal(getLearnedReasoningEffort("acme", "model-x"), "low");
const learned = recordLearnedReasoningEffort("acme", "model-x", [
"none",
"low",
]) as unknown as Set<string>;
assert.equal(learned.size, 2);
assert.ok(learned.has("low"));
assert.equal((getLearnedReasoningEffort("acme", "model-x") as unknown as Set<string>).size, 2);
});
test("clampToLearned medium→low when accepted is low,high,max", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("medium", new Set(["low", "high", "max"])), "low");
});
test("clampToLearned xhigh→high when accepted is low,high,max", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("xhigh", new Set(["low", "high", "max"])), "high");
});
test("clampToLearned ultra→max when accepted is low,high,max", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("ultra", new Set(["low", "high", "max"])), "max");
});
test("clampToLearned ultra→medium when accepted is low,medium", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("ultra", new Set(["low", "medium"])), "medium");
});
test("clampToLearned high→medium when accepted is low,medium", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("high", new Set(["low", "medium"])), "medium");
});
test("clampToLearned returns null when already accepted", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("low", new Set(["low", "high", "max"])), null);
});
test("clampToLearned returns null when effort < min (no upgrade)", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("low", new Set(["high", "max"])), null);
});
test("clampToLearned returns null for turbo (not in ORDER)", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("turbo", new Set(["low", "high", "max"])), null);
});
test("clampToLearned returns null when effort is none but accepted is low,high,max", async () => {
const { clampToLearned } = await import("../../open-sse/services/learnedReasoningEffortCaps.ts");
assert.equal(clampToLearned("none", new Set(["low", "high", "max"])), null);
});
test("recordLearned stores Set and getLearned returns Set", () => {
const s = recordLearnedReasoningEffort("acme", "m1", ["low", "high", "max"]);
assert.ok(s instanceof Set);
assert.deepEqual([...(s as unknown as Set<string>)].sort(), ["high", "low", "max"]);
const g = getLearnedReasoningEffort("acme", "m1");
assert.ok(g instanceof Set);
});
test("monotonicity incomparable: keep existing when neither subset", () => {
recordLearnedReasoningEffort("acme", "m4", ["low", "high", "max"]);
const s4 = recordLearnedReasoningEffort("acme", "m4", ["low", "medium"]);
assert.equal((s4 as unknown as Set<string>).size, 3);
assert.ok((s4 as unknown as Set<string>).has("high"));
});
test("getLearnedReasoningEffort returns null for unknown provider+model", () => {
assert.equal(getLearnedReasoningEffort("acme", "unknown-model"), null);
});
test("getLearnedReasoningEffort is keyed case-insensitively on provider+model", () => {
recordLearnedReasoningEffort("OVH", "Qwen3-Coder-30B", ["none", "high"]);
assert.equal(getLearnedReasoningEffort("ovh", "qwen3-coder-30b"), "high");
assert.equal(getLearnedReasoningEffort("OVH", "QWEN3-CODER-30B"), "high");
assert.ok(
(getLearnedReasoningEffort("ovh", "qwen3-coder-30b") as unknown as Set<string>).has("high")
);
assert.ok(
(getLearnedReasoningEffort("OVH", "QWEN3-CODER-30B") as unknown as Set<string>).has("high")
);
});
test("different providers for the same model id have independent caps", () => {
@@ -124,3 +206,45 @@ test("handles empty/null provider or model gracefully", () => {
assert.equal(recordLearnedReasoningEffort("", "m", ["high"]), null);
assert.equal(recordLearnedReasoningEffort("p", "", ["high"]), null);
});
// ── getLearnedReasoningEffortForModel ────────────────────────────────────────
import { getLearnedReasoningEffortForModel } from "../../open-sse/services/learnedReasoningEffortCaps.ts";
test("getLearnedReasoningEffortForModel finds a set recorded under any provider key", () => {
recordLearnedReasoningEffort("openai-compatible-chat-eaff6869", "X-Preview-F-Free", [
"low",
"high",
"max",
]);
const set = getLearnedReasoningEffortForModel("x-preview-f-free");
assert.ok(set);
assert.deepEqual([...set].sort(), ["high", "low", "max"]);
});
test("getLearnedReasoningEffortForModel intersects when multiple providers disagree", () => {
recordLearnedReasoningEffort("conn-a", "shared-model", ["low", "high", "max"]);
recordLearnedReasoningEffort("conn-b", "shared-model", ["low"]);
const set = getLearnedReasoningEffortForModel("shared-model");
assert.ok(set);
assert.deepEqual([...set], ["low"]);
});
test("getLearnedReasoningEffortForModel returns null when nothing learned or empty model", () => {
assert.equal(getLearnedReasoningEffortForModel("never-learned"), null);
assert.equal(getLearnedReasoningEffortForModel(""), null);
assert.equal(getLearnedReasoningEffortForModel(undefined), null);
});
test("recordLearnedReasoningEffort warns when every token is unrecognized", () => {
const warnings: string[] = [];
const orig = console.warn;
console.warn = (msg: string) => warnings.push(msg);
try {
const result = recordLearnedReasoningEffort("p", "m", ["bogus-one", "bogus-two"]);
assert.equal(result, null);
assert.ok(warnings.some((w) => w.includes("reasoning_effort") && w.includes("bogus-one")));
} finally {
console.warn = orig;
}
});

View File

@@ -0,0 +1,144 @@
import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
const TEST_DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-lmstudio-embedding-11233-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const { parseEmbeddingModel } = await import("../../open-sse/config/embeddingRegistry.ts");
const { handleEmbedding } = await import("../../open-sse/handlers/embeddings.ts");
const core = await import("../../src/lib/db/core.ts");
const { createProviderConnection } = await import("../../src/lib/db/providers.ts");
const { createEmbeddingResponse } = await import("../../src/lib/embeddings/service.ts");
test.after(() => {
core.resetDbInstance();
rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
// Issue #11233: the dashboard stores LM Studio connections under the provider
// id "lm-studio" (hyphenated), but the embedding registry keys the provider as
// "lmstudio" with no alias. Two symptoms resulted:
// 1. "lm-studio/<model>" embedding requests failed with 400 unknown provider.
// 2. "lmstudio/<model>" requests always hit the hardcoded localhost:1234
// endpoint, ignoring the baseUrl of the configured connection.
// The fix mirrors the ollama-local pattern from #2824/#9225: an embedding
// provider alias plus optional (non-auth) connection hydration and the same
// baseUrl normalization in the handler.
test("lm-studio model strings resolve to the lmstudio embedding provider", () => {
assert.deepEqual(parseEmbeddingModel("lm-studio/nomic-embed-text"), {
provider: "lmstudio",
model: "nomic-embed-text",
});
});
test("lmstudio routes to the configured connection baseUrl", async () => {
const originalFetch = globalThis.fetch;
let capturedUrl: string | null = null;
globalThis.fetch = async (url) => {
capturedUrl = String(url);
return new Response(
JSON.stringify({
data: [{ object: "embedding", embedding: [0.1, 0.2], index: 0 }],
usage: { prompt_tokens: 2, total_tokens: 2 },
}),
{ status: 200, headers: { "content-type": "application/json" } }
);
};
try {
const result = await handleEmbedding({
body: { model: "lmstudio/nomic-embed-text", input: "hello" },
resolvedProvider: {
id: "lmstudio",
baseUrl: "http://localhost:1234/v1/embeddings",
authType: "none",
authHeader: "none",
models: [],
},
resolvedModel: "nomic-embed-text",
credentials: {
providerSpecificData: { baseUrl: "http://192.168.1.50:1234/v1" },
},
log: null,
});
assert.equal(result.success, true);
} finally {
globalThis.fetch = originalFetch;
}
assert.equal(capturedUrl, "http://192.168.1.50:1234/v1/embeddings");
});
test("lmstudio keeps the static localhost default without credentials", async () => {
const originalFetch = globalThis.fetch;
let capturedUrl: string | null = null;
globalThis.fetch = async (url) => {
capturedUrl = String(url);
return new Response(
JSON.stringify({
data: [{ object: "embedding", embedding: [0.3, 0.4], index: 0 }],
usage: { prompt_tokens: 2, total_tokens: 2 },
}),
{ status: 200, headers: { "content-type": "application/json" } }
);
};
try {
const result = await handleEmbedding({
body: { model: "lmstudio/nomic-embed-text", input: "hello" },
credentials: null,
log: null,
});
assert.equal(result.success, true);
} finally {
globalThis.fetch = originalFetch;
}
assert.equal(capturedUrl, "http://localhost:1234/v1/embeddings");
});
test("lmstudio service hydrates the lm-studio connection host without requiring a key", async () => {
await createProviderConnection({
provider: "lm-studio",
authType: "none",
name: "LAN LM Studio",
isActive: true,
providerSpecificData: { baseUrl: "http://10.20.0.60:1234/v1/" },
});
const originalFetch = globalThis.fetch;
let captured: { url: string; headers: Record<string, string> } | null = null;
globalThis.fetch = async (url, options = {}) => {
captured = {
url: String(url),
headers: (options.headers as Record<string, string>) || {},
};
return new Response(
JSON.stringify({
data: [{ object: "embedding", embedding: [0.5, 0.6], index: 0 }],
usage: { prompt_tokens: 2, total_tokens: 2 },
}),
{ status: 200, headers: { "content-type": "application/json" } }
);
};
try {
const response = await createEmbeddingResponse({
model: "lm-studio/nomic-embed-text",
input: "hello",
});
assert.equal(response.status, 200);
} finally {
globalThis.fetch = originalFetch;
}
assert.ok(captured);
assert.equal(captured.url, "http://10.20.0.60:1234/v1/embeddings");
assert.equal(captured.headers.Authorization, undefined);
});

View File

@@ -6,6 +6,7 @@ import {
buildSessionsSummary,
buildTelemetryPayload,
projectAdaptiveAdmissionSummary,
projectChatAdmissionSummary,
} from "../../src/lib/monitoring/observability.ts";
test("buildSessionsSummary returns sticky counts and ordered top sessions", () => {
@@ -336,3 +337,71 @@ test("buildHealthPayload projects allowlisted adaptiveAdmission aggregates only"
assert.equal(projectAdaptiveAdmissionSummary(null), null);
assert.equal(projectAdaptiveAdmissionSummary(undefined), null);
});
// #11244: the STRUCTURAL chat-admission gate (chatBodyAdmission.ts) must surface in
// the health payload next to — never instead of — the adaptive snapshot, with only
// the documented low-cardinality fields projected.
test("buildHealthPayload projects allowlisted structural chatAdmission fields only", () => {
const snapshot = {
activeHeavy: 1,
activeHealthyHeadroom: 1,
waiting: 2,
queuedBytes: 524_288,
shedTotal: 3,
shedsByReason: { queue_timeout: 2, queued_bytes_budget: 1 },
lanes: [
{ key: "key_c49d1c242feda590", waiting: 1 },
{ key: "anonymous", waiting: 1 },
],
// Extra keys that must never leak into the public payload.
internalController: { secret: "controller-state" },
rawAuthorization: "Bearer raw-SHOULD-NOT-LEAK",
} as unknown as import("../../src/lib/monitoring/observability.ts").ChatAdmissionSnapshot;
const payload = buildHealthPayload({
appVersion: "9.9.9",
settings: { setupComplete: false },
connections: [],
circuitBreakers: [],
rateLimitStatus: {},
learnedLimits: {},
lockouts: {},
localProviders: {},
inflightRequests: 0,
quotaMonitorSummary: {
active: 0,
alerting: 0,
exhausted: 0,
errors: 0,
statusCounts: { starting: 0, idle: 0, healthy: 0, warning: 0, exhausted: 0, error: 0 },
byProvider: {},
},
quotaMonitorMonitors: [],
activeSessions: [],
chatAdmission: snapshot,
});
assert.deepEqual(payload.chatAdmission, {
activeHeavy: 1,
activeHealthyHeadroom: 1,
waiting: 2,
queuedBytes: 524_288,
shedTotal: 3,
shedsByReason: { queue_timeout: 2, queued_bytes_budget: 1 },
lanes: [
{ key: "key_c49d1c242feda590", waiting: 1 },
{ key: "anonymous", waiting: 1 },
],
});
// The adaptive projection is untouched by the new key.
assert.equal(payload.adaptiveAdmission, null);
const json = JSON.stringify(payload);
assert.equal(json.includes("controller-state"), false);
assert.equal(json.includes("raw-SHOULD-NOT-LEAK"), false);
assert.equal(json.includes("internalController"), false);
// Absent / null snapshot projects to null (degraded path parity).
assert.equal(projectChatAdmissionSummary(null), null);
assert.equal(projectChatAdmissionSummary(undefined), null);
});

View File

@@ -0,0 +1,152 @@
// #11226 — OpenRouter key validation was vacuous: the probe targeted the PUBLIC
// /api/v1/models endpoint, which answers 200 to any key (or no key at all), so a
// bad key was saved as "valid" and only failed later on real chat traffic with the
// upstream 401 "User not found.". The authenticated key-info endpoint
// (/api/v1/auth/key) is the correct probe: 200 = valid, 401 = invalid.
//
// The fetch stubs below mimic the REAL OpenRouter behavior verified live:
// GET /api/v1/models → 200 without any auth (public catalog)
// GET /api/v1/auth/key → 401 {"error":{"message":"User not found.","code":401}} for a bad key
import { describe, it } from "node:test";
import assert from "node:assert/strict";
const { getRegistryEntry } = await import("../../open-sse/config/providerRegistry.ts");
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
const { testProviderApiKey } = await import("../../bin/cli/provider-test.mjs");
const AUTH_KEY_URL = "https://openrouter.ai/api/v1/auth/key";
const PUBLIC_MODELS_URL = "https://openrouter.ai/api/v1/models";
const BAD_KEY = "sk-or-v1-definitely-invalid-key";
const GOOD_KEY = "sk-or-v1-valid-key";
interface RecordedCall {
url: string;
authorization: string | null;
}
/**
* Stub fetch with the real OpenRouter behavior: /models is public (always 200),
* /auth/key requires a valid bearer (401 "User not found." otherwise).
*/
function stubRealOpenRouter() {
const calls: RecordedCall[] = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) => {
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
const headers = new Headers(
init?.headers ?? (input instanceof Request ? input.headers : undefined)
);
calls.push({ url, authorization: headers.get("authorization") });
if (url.startsWith(AUTH_KEY_URL)) {
const bearer = headers.get("authorization") || "";
if (bearer === `Bearer ${GOOD_KEY}`) {
return new Response(JSON.stringify({ data: { label: "ok", is_free_tier: false } }), {
status: 200,
});
}
return new Response(JSON.stringify({ error: { message: "User not found.", code: 401 } }), {
status: 401,
});
}
if (url.includes("/models")) {
// Public catalog — answers 200 regardless of the Authorization header.
return new Response(JSON.stringify({ data: [] }), { status: 200 });
}
return new Response("{}", { status: 404 });
}) as typeof fetch;
return {
calls,
restore: () => {
globalThis.fetch = originalFetch;
},
};
}
describe("openrouter registry — authenticated key-validation endpoint (#11226)", () => {
it("declares the authenticated /auth/key probe as its key-test endpoint", () => {
const entry = getRegistryEntry("openrouter");
assert.ok(entry, "openrouter must be registered in the execution registry");
assert.equal(entry.testKeyModelsUrl, AUTH_KEY_URL);
});
it("marks a bad key INVALID even though the public /models endpoint answers 200", async () => {
const stub = stubRealOpenRouter();
try {
const result = await validateProviderApiKey({ provider: "openrouter", apiKey: BAD_KEY });
assert.equal(result.valid, false, "bad key must not validate against the public catalog");
assert.equal(result.error, "Invalid API key");
assert.deepEqual(
stub.calls.map((c) => c.url),
[AUTH_KEY_URL],
"must probe the authenticated key endpoint, not the public /models"
);
assert.equal(stub.calls[0].authorization, `Bearer ${BAD_KEY}`);
} finally {
stub.restore();
}
});
it("marks a good key VALID via /auth/key and never falls back to the chat probe", async () => {
const stub = stubRealOpenRouter();
try {
const result = await validateProviderApiKey({ provider: "openrouter", apiKey: GOOD_KEY });
assert.equal(result.valid, true);
assert.equal(result.error, null);
assert.deepEqual(
stub.calls.map((c) => c.url),
[AUTH_KEY_URL]
);
} finally {
stub.restore();
}
});
});
describe("omniroute providers test — openrouter probe (#11226)", () => {
it("marks a bad key INVALID even though the public /models endpoint answers 200", async () => {
const stub = stubRealOpenRouter();
try {
const result = await testProviderApiKey({ provider: "openrouter", apiKey: BAD_KEY });
assert.equal(result.valid, false, "CLI test must not trust the public /models endpoint");
assert.equal(result.error, "Invalid API key");
assert.deepEqual(
stub.calls.map((c) => c.url),
[AUTH_KEY_URL]
);
} finally {
stub.restore();
}
});
it("marks a good key VALID via /auth/key", async () => {
const stub = stubRealOpenRouter();
try {
const result = await testProviderApiKey({ provider: "openrouter", apiKey: GOOD_KEY });
assert.equal(result.valid, true);
assert.equal(result.error, null);
assert.deepEqual(
stub.calls.map((c) => c.url),
[AUTH_KEY_URL]
);
} finally {
stub.restore();
}
});
it("does not change the probe for other OpenAI-like providers (openai still uses /models)", async () => {
const stub = stubRealOpenRouter();
try {
const result = await testProviderApiKey({ provider: "openai", apiKey: GOOD_KEY });
assert.equal(result.valid, true);
assert.deepEqual(
stub.calls.map((c) => c.url),
["https://api.openai.com/v1/models"]
);
assert.ok(!stub.calls.some((c) => c.url === PUBLIC_MODELS_URL));
} finally {
stub.restore();
}
});
});

View File

@@ -0,0 +1,78 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
REQUIRED_MACHINE_TOKEN_RUNTIME_FILES,
REQUIRED_SQLJS_RUNTIME_FILES,
} from "../../scripts/check/check-pack-boot.mjs";
import { PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS } from "../../scripts/build/pack-artifact-policy.ts";
import * as sqliteRuntime from "../../bin/cli/runtime/sqliteRuntime.mjs";
// Coherence guard for the v3.8.50 publish blocker (#11242): check:pack-artifact
// FAILS any tarball path containing a node_modules segment (files[] excludes them
// via "!**/node_modules/**"), while check:pack-boot REQUIRED sql.js under the
// vendored dist/node_modules/ location — a path the tarball can never contain,
// so the two gates could never be green at the same time. The npm packaging
// model is now dependency-based: sql.js and node-machine-id are declared
// `dependencies` that a clean install places under <packageRoot>/node_modules/,
// and better-sqlite3 is an optionalDependency installed natively per platform.
// These tests pin that contract so neither gate can drift back into conflict.
const REPO_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
const PKG = JSON.parse(readFileSync(path.join(REPO_ROOT, "package.json"), "utf8")) as {
dependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
};
test("pack-boot required runtime files never reference a never-publishable vendored path", () => {
const requiredFiles = [...REQUIRED_SQLJS_RUNTIME_FILES, ...REQUIRED_MACHINE_TOKEN_RUNTIME_FILES];
assert.ok(requiredFiles.length > 0, "pack-boot must require at least one runtime file");
for (const requiredPath of requiredFiles) {
for (const segment of PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS) {
const vendoredPrefix = `dist/${segment}/`;
assert.ok(
!requiredPath.includes(vendoredPrefix),
`"${requiredPath}" lives under ${vendoredPrefix} — check:pack-artifact bans any ` +
`tarball path with a "${segment}" segment, so check:pack-boot must require the ` +
`dependency-installed location (node_modules/<pkg>) instead (#11242)`
);
}
}
});
test("sql.js and node-machine-id are declared runtime dependencies (npm installs them)", () => {
assert.ok(
PKG.dependencies?.["sql.js"],
"sql.js must stay in dependencies so a clean install provides node_modules/sql.js"
);
assert.ok(
PKG.dependencies?.["node-machine-id"],
"node-machine-id must stay in dependencies so a clean install provides node_modules/node-machine-id"
);
});
test("the lazy better-sqlite3 runtime install targets the declared optionalDependency major", () => {
const spec = (sqliteRuntime as Record<string, unknown>).BETTER_SQLITE3_VERSION;
assert.equal(
typeof spec,
"string",
"bin/cli/runtime/sqliteRuntime.mjs must export BETTER_SQLITE3_VERSION"
);
const declared = PKG.optionalDependencies?.["better-sqlite3"];
assert.ok(declared, "package.json must declare better-sqlite3 as an optionalDependency");
const majorOf = (versionSpec: string): number => {
const match = versionSpec.match(/(\d+)\./);
assert.ok(match, `"${versionSpec}" must contain a semver major`);
return Number(match[1]);
};
assert.equal(
majorOf(spec as string),
majorOf(declared),
`lazy runtime install "${spec}" drifted from optionalDependencies.better-sqlite3 ` +
`"${declared}" — the fallback install must track the same major (#11242)`
);
});

View File

@@ -5,7 +5,8 @@
* iteration order silently won, emitting a startup warning and shadowing a real
* provider:
* - "kimi" → kimi-web (shadowed the kimi provider that gained a dedicated executor)
* - "hc" → hackclub (shadowed huggingchat)
* - "hc" → the provider that held it shadowed huggingchat (it was later
* removed entirely, #11176; huggingchat keeps its own id as alias)
*
* The decision: the primary provider keeps the short alias; the web/secondary
* variant takes its own id as alias. This test pins both the global uniqueness

View File

@@ -11,9 +11,23 @@ function parse(overrides: unknown) {
}
test("rateLimitOverrides: valid object with all fields", () => {
const r = parse({ rpm: 100, tpm: 50000, tpd: 1000000, minTime: 100, maxConcurrent: 5 });
const r = parse({
rpm: 100,
tpm: 50000,
tpd: 1000000,
minTime: 100,
maxConcurrent: 5,
maxWaitMs: 45000,
});
assert.ok(r.success, String(r.error));
assert.deepEqual(r.data.rateLimitOverrides, { rpm: 100, tpm: 50000, tpd: 1000000, minTime: 100, maxConcurrent: 5 });
assert.deepEqual(r.data.rateLimitOverrides, {
rpm: 100,
tpm: 50000,
tpd: 1000000,
minTime: 100,
maxConcurrent: 5,
maxWaitMs: 45000,
});
});
test("rateLimitOverrides: partial fields", () => {
@@ -71,3 +85,32 @@ test("rateLimitOverrides: all zeros is valid", () => {
const r = parse({ rpm: 0, tpm: 0, tpd: 0, minTime: 0, maxConcurrent: 0 });
assert.ok(r.success, String(r.error));
});
test("rateLimitOverrides: valid maxWaitMs", () => {
const r = parse({ maxWaitMs: 45000 });
assert.ok(r.success, String(r.error));
assert.deepEqual(r.data.rateLimitOverrides, { maxWaitMs: 45000 });
});
test("rateLimitOverrides: maxWaitMs coerced from string", () => {
const r = parse({ maxWaitMs: "30000" });
assert.ok(r.success, String(r.error));
assert.equal(r.data.rateLimitOverrides.maxWaitMs, 30000);
});
test("rateLimitOverrides: rejects negative maxWaitMs", () => {
assert.equal(parse({ maxWaitMs: -1 }).success, false);
});
test("rateLimitOverrides: rejects float maxWaitMs", () => {
assert.equal(parse({ maxWaitMs: 1.5 }).success, false);
});
test("rateLimitOverrides: rejects maxWaitMs above 120000 ceiling", () => {
assert.equal(parse({ maxWaitMs: 120001 }).success, false);
});
test("rateLimitOverrides: maxWaitMs of 0 is valid (no override)", () => {
const r = parse({ maxWaitMs: 0 });
assert.ok(r.success, String(r.error));
});

View File

@@ -3,7 +3,7 @@
* (free tier via Discord signup).
*
* Verifies the new provider is wired end-to-end the same way as the other
* aggregator/gateway providers (hackclub, chutes, glhf, ...):
* aggregator/gateway providers (chutes, glhf, ...):
* - present in the executor REGISTRY with an OpenAI-compatible shape
* - resolvable through getExecutor() (falls through to DefaultExecutor,
* same as every other `executor: "default"` registry entry)
@@ -41,7 +41,7 @@ test("#6670 freetheai resolves through getExecutor() as a DefaultExecutor instan
test("#6670 freetheai is classified as an aggregator/gateway provider", () => {
assert.ok(
AGGREGATOR_PROVIDER_IDS.has("freetheai"),
"freetheai must be listed in AGGREGATOR_PROVIDER_IDS alongside hackclub/chutes/etc"
"freetheai must be listed in AGGREGATOR_PROVIDER_IDS alongside chutes/etc"
);
});

View File

@@ -26,6 +26,7 @@
// merge-train batch — independently bumped the gateways family too, landing at 231; Freebuff
// (gateways, #10531) brings it to 232. #8864 moves uncloseai (gateways family) into
// NOAUTH_PROVIDERS, dropping the APIKEY_PROVIDERS count to 231. Logfare (gateways, #10987) brings it back to 232.
// #11176 removes hackclub (gateways family), landing at 231.
import { test } from "node:test";
import assert from "node:assert/strict";
@@ -54,12 +55,12 @@ test("barrel still exports every catalog + key helpers", () => {
}
});
test("APIKEY_PROVIDERS merges the 6 family files into 232 entries (no loss / no dup)", async () => {
test("APIKEY_PROVIDERS merges the 6 family files into 231 entries (no loss / no dup)", async () => {
const keys = Object.keys((P as Record<string, object>).APIKEY_PROVIDERS);
assert.equal(keys.length, 232);
assert.equal(new Set(keys).size, 232, "duplicate keys after spread-merge");
assert.equal(keys.length, 231);
assert.equal(new Set(keys).size, 231, "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 232.
// strict partition (every provider in exactly one), so the sum must be exactly 231.
const families: [string, string][] = [
["gateways", "APIKEY_PROVIDERS_GATEWAYS"],
["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"],
@@ -79,7 +80,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 232 entries (no loss / no
seen.add(k);
}
}
assert.equal(famTotal, 232, "families must partition all 232 providers");
assert.equal(famTotal, 231, "families must partition all 231 providers");
});
test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {

View File

@@ -2,7 +2,7 @@
* Issue #6674 — Add Naga.ac and ChatAnywhere as gpt4free-ecosystem aggregator providers.
*
* Verifies both providers are wired end-to-end the same way as the other aggregator
* gateway providers (g4f-groq, freetheai, hackclub):
* gateway providers (g4f-groq, freetheai):
* - present in the executor REGISTRY with an OpenAI-compatible shape
* - resolvable through getExecutor() (falls through to DefaultExecutor)
* - listed in AGGREGATOR_PROVIDER_IDS so they show up in the aggregator category

View File

@@ -0,0 +1,303 @@
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";
/**
* #11234 — opencode-go quota preflight ignored the dashboard quota snapshots.
*
* Root cause (two gaps):
*
* A) `fetchOpencodeQuota` (open-sse/services/opencodeQuotaFetcher.ts) only
* consulted the live upstream endpoint, which has no public quota API
* (404 — see module JSDoc). It never read the quota snapshots the
* dashboard scrape (`getOpenCodeGoUsage`, keyed session/weekly/mcp_monthly)
* persists through `src/domain/quotaCache.ts`. Every preflight therefore
* evaluated `null` and proceeded (fail-open), even with a sister
* connection sitting at 0% weekly remaining in plain sight on the
* dashboard.
*
* B) The sibling-selection latency gate in
* `src/sse/services/auth.ts::getProviderCredentialsWithQuotaPreflight`
* never consulted `resilience.quotaPreflight.enabled`
* (QUOTA_PREFLIGHT_CUTOFF_ENABLED). That flag only armed the auto-strategy
* candidate builder and the per-target cutoff for pinned connections, so
* a priority combo over sibling opencode-go connections (connectionId
* null at combo level) skipped preflight entirely.
*
* Fix:
* A) The fetcher now synthesizes its triple-window quota from the cached
* dashboard snapshots (read-only, accessors only, no re-scrape on the hot
* path) when the live endpoint yields nothing — mapping
* session→window_5h, weekly→window_weekly, mcp_monthly→window_monthly and
* mirroring `getQuotaWindowStatus` semantics (expired resetAt = window has
* rolled over = must not count as exhausted).
* B) `resilience.quotaPreflight.enabled === true` now arms the
* sibling-selection latency gate as well.
*
* These tests are the regression guards: fetcher-level for (A), selector-level
* for (B).
*/
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omni-quota-11234-"));
process.env.DATA_DIR = TEST_DATA_DIR;
// Part (B): the operator flag must be ON before the resilience settings module
// is first imported (its defaults are computed at module load).
process.env.QUOTA_PREFLIGHT_CUTOFF_ENABLED = "true";
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "quota-11234-secret";
const originalFetch = globalThis.fetch;
const coreDb = await import("../../src/lib/db/core.ts");
const quotaSnapshotsDb = await import("../../src/lib/db/quotaSnapshots.ts");
const quotaCache = await import("../../src/domain/quotaCache.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const { fetchOpencodeQuota, invalidateOpencodeQuotaCache } = await import(
"../../open-sse/services/opencodeQuotaFetcher.ts"
);
const { evaluateQuotaCutoff, registerQuotaFetcher } = await import(
"../../open-sse/services/quotaPreflight.ts"
);
const { buildAutoQuotaThresholds } = await import(
"../../open-sse/services/combo/quotaExhaustionCutoff.ts"
);
const { resolveResilienceSettings } = await import("../../src/lib/resilience/settings.ts");
const auth = await import("../../src/sse/services/auth.ts");
const PROVIDER = "opencode-go";
// Dashboard scrape window keys (opencodeOllamaUsage.ts::OPENCODE_GO_QUOTA_ORDER)
const DASH_SESSION = "session";
const DASH_WEEKLY = "weekly";
// Fetcher/preflight window keys (opencodeQuotaFetcher.ts registry)
const WINDOW_5H = "window_5h";
const WINDOW_WEEKLY = "window_weekly";
function seedSnapshot(
connectionId: string,
windowKey: string,
remainingPercentage: number,
nextResetAt: string | null
) {
quotaSnapshotsDb.saveQuotaSnapshot({
provider: PROVIDER,
connection_id: connectionId,
window_key: windowKey,
remaining_percentage: remainingPercentage,
is_exhausted: remainingPercentage <= 0 ? 1 : 0,
next_reset_at: nextResetAt,
window_duration_ms: null,
raw_data: null,
});
}
function dashboardConfiguredConnection(apiKey: string): Record<string, unknown> {
// Mirrors the operator-configured dashboard scrape
// (opencodeOllamaUsage.ts::resolveOpenCodeGoDashboardConfig).
return {
apiKey,
providerSpecificData: {
openCodeGoWorkspaceId: "ws-11234",
openCodeGoAuthCookie: "auth-cookie-11234",
},
};
}
function hoursFromNow(hours: number): string {
return new Date(Date.now() + hours * 3_600_000).toISOString();
}
test.after(() => {
globalThis.fetch = originalFetch;
coreDb.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test.afterEach(() => {
globalThis.fetch = originalFetch;
quotaCache.__clearForTests();
});
// ─── (A) fetcher bridge: dashboard snapshots → QuotaInfo ────────────────────
test("#11234 dashboard snapshots feed the quota cutoff when the live endpoint has no quota API", async () => {
const connectionId = `oc-11234-block-${Date.now()}`;
let fetchCalls = 0;
globalThis.fetch = async () => {
fetchCalls += 1;
return new Response(null, { status: 404 });
};
// Dashboard shows: weekly fully drained (0% remaining, reset in 3 days),
// session healthy (80% remaining).
seedSnapshot(connectionId, DASH_WEEKLY, 0, hoursFromNow(72));
seedSnapshot(connectionId, DASH_SESSION, 80, hoursFromNow(2));
const quota = await fetchOpencodeQuota(connectionId, dashboardConfiguredConnection("sk-test"));
assert.ok(quota, "fetcher must synthesize quota from dashboard snapshots when the live endpoint 404s");
assert.equal(fetchCalls, 1, "snapshot bridge must be read-only — no re-scrape on the hot path");
// Key mapping: weekly → window_weekly (0% remaining = 100% used),
// session → window_5h (80% remaining = 20% used).
assert.equal(quota.windows?.[WINDOW_WEEKLY]?.percentUsed, 1);
assert.ok(
Math.abs((quota.windows?.[WINDOW_5H]?.percentUsed ?? 0) - 0.2) < 1e-9,
`window_5h percentUsed should be ~0.2, got ${quota.windows?.[WINDOW_5H]?.percentUsed}`
);
const decision = evaluateQuotaCutoff(
quota,
buildAutoQuotaThresholds(PROVIDER, undefined, null)
);
assert.equal(decision.proceed, false, "weekly at 0% remaining must block the connection");
assert.equal(decision.reason, "quota_exhausted");
invalidateOpencodeQuotaCache(connectionId);
});
test("#11234 a snapshot whose reset already passed must not count as exhausted", async () => {
const connectionId = `oc-11234-expired-${Date.now()}`;
globalThis.fetch = async () => new Response(null, { status: 404 });
// Weekly hit 0% but its reset is 1h in the PAST — the window rolled into a
// fresh period, so the stale 0% must not block (mirrors
// getQuotaWindowStatus: expired resetAt → reachedThreshold = false).
seedSnapshot(connectionId, DASH_WEEKLY, 0, hoursFromNow(-1));
seedSnapshot(connectionId, DASH_SESSION, 80, hoursFromNow(2));
const quota = await fetchOpencodeQuota(connectionId, dashboardConfiguredConnection("sk-test"));
assert.ok(quota, "the healthy session snapshot should still synthesize");
assert.equal(
quota.windows?.[WINDOW_WEEKLY],
undefined,
"an expired weekly window must be dropped from the synthesized quota"
);
const decision = evaluateQuotaCutoff(
quota,
buildAutoQuotaThresholds(PROVIDER, undefined, null)
);
assert.equal(decision.proceed, true, "an expired weekly window must not block the connection");
invalidateOpencodeQuotaCache(connectionId);
});
test("#11234 per-window threshold overrides apply to the mapped window_weekly key", async () => {
const connectionId = `oc-11234-threshold-${Date.now()}`;
globalThis.fetch = async () => new Response(null, { status: 404 });
// Weekly at 40% remaining — above the factory 2% cutoff (would proceed),
// but below an operator override of 50% min-remaining for window_weekly.
seedSnapshot(connectionId, DASH_WEEKLY, 40, hoursFromNow(72));
seedSnapshot(connectionId, DASH_SESSION, 90, hoursFromNow(2));
const quota = await fetchOpencodeQuota(connectionId, dashboardConfiguredConnection("sk-test"));
assert.ok(quota);
const factoryDecision = evaluateQuotaCutoff(
quota,
buildAutoQuotaThresholds(PROVIDER, undefined, null)
);
assert.equal(
factoryDecision.proceed,
true,
"factory 2% cutoff must not block a window at 40% remaining"
);
const settings = resolveResilienceSettings({
resilienceSettings: {
quotaPreflight: {
enabled: true,
providerWindowDefaults: { [PROVIDER]: { [WINDOW_WEEKLY]: 50 } },
},
},
});
const overrideDecision = evaluateQuotaCutoff(
quota,
buildAutoQuotaThresholds(PROVIDER, undefined, settings)
);
assert.equal(
overrideDecision.proceed,
false,
"a 50% window_weekly override must block at 40% remaining — the override resolves against the mapped key"
);
invalidateOpencodeQuotaCache(connectionId);
});
test("#11234 fail-open preserved: configured dashboard with no snapshots still returns null", async () => {
const connectionId = `oc-11234-failopen-${Date.now()}`;
globalThis.fetch = async () => new Response(null, { status: 404 });
const quota = await fetchOpencodeQuota(connectionId, dashboardConfiguredConnection("sk-test"));
assert.equal(quota, null, "no snapshots → fail-open (null), exactly as before");
invalidateOpencodeQuotaCache(connectionId);
});
// ─── (B) flag scope: sibling-selection latency gate ─────────────────────────
test("#11234 quotaPreflight.enabled arms sibling selection: the exhausted sister is skipped for the healthy one", async () => {
const tag = Date.now();
const exhausted = await providersDb.createProviderConnection({
provider: PROVIDER,
authType: "apikey",
name: `oc-11234-exhausted-${tag}`,
apiKey: "sk-oc-11234-exhausted",
priority: 1,
isActive: true,
testStatus: "active",
});
const healthy = await providersDb.createProviderConnection({
provider: PROVIDER,
authType: "apikey",
name: `oc-11234-healthy-${tag}`,
apiKey: "sk-oc-11234-healthy",
priority: 2,
isActive: true,
testStatus: "active",
});
// Stub the upstream quota signal: the priority-1 sister is fully drained,
// the priority-2 sister is healthy. No per-connection overrides, no
// per-(provider, window) defaults, no legacy quotaPreflightEnabled flag,
// factory 2% global threshold — so TODAY the latency gate skips preflight
// entirely and the selector returns the exhausted sister. With
// resilience.quotaPreflight.enabled arming the gate, preflight must run and
// skip her.
registerQuotaFetcher(PROVIDER, async (connectionId: string) => {
if (connectionId === exhausted.id) {
return {
used: 100,
total: 100,
percentUsed: 1.0,
resetAt: hoursFromNow(1),
};
}
return { used: 0, total: 100, percentUsed: 0, resetAt: null };
});
try {
const selection = await auth.getProviderCredentialsWithQuotaPreflight(
PROVIDER,
null,
null,
null
);
const result = selection as { connectionId?: string } | null;
assert.equal(
result?.connectionId,
healthy.id,
"with quotaPreflight.enabled the selector must skip the exhausted priority-1 sister and pick the healthy one"
);
} finally {
await providersDb.deleteProviderConnection(exhausted.id);
await providersDb.deleteProviderConnection(healthy.id);
}
});

View File

@@ -189,6 +189,48 @@ test("#6593 zai-web receives a provider-scoped 60s scheduling budget", () => {
assert.equal(rateLimitManager.resolveRequestQueueMaxWaitMs("ZAI-WEB", 90_000), 90_000);
});
test("#6593 connection maxWaitMs override takes priority over the zai-web scheduling budget", () => {
rateLimitManager.refreshConnectionRateLimits("conn-maxwait-override", { maxWaitMs: 45_000 });
try {
// Non-special provider: override wins over the passed-in configured default.
assert.equal(
rateLimitManager.resolveRequestQueueMaxWaitMs("openai", 15_000, "conn-maxwait-override"),
45_000
);
// zai-web: override wins over its hardcoded 60s floor too.
assert.equal(
rateLimitManager.resolveRequestQueueMaxWaitMs("zai-web", 15_000, "conn-maxwait-override"),
45_000
);
} finally {
rateLimitManager.refreshConnectionRateLimits("conn-maxwait-override", null);
}
});
test("#6593 a connection without a maxWaitMs override keeps the zai-web 60s floor", () => {
rateLimitManager.refreshConnectionRateLimits("conn-no-maxwait-override", { rpm: 10 });
try {
assert.equal(
rateLimitManager.resolveRequestQueueMaxWaitMs("zai-web", 15_000, "conn-no-maxwait-override"),
rateLimitManager.ZAI_WEB_REQUEST_QUEUE_MAX_WAIT_MS
);
} finally {
rateLimitManager.refreshConnectionRateLimits("conn-no-maxwait-override", null);
}
});
test("#6593 a maxWaitMs override of 0 is treated as no override", () => {
rateLimitManager.refreshConnectionRateLimits("conn-zero-maxwait-override", { maxWaitMs: 0 });
try {
assert.equal(
rateLimitManager.resolveRequestQueueMaxWaitMs("openai", 15_000, "conn-zero-maxwait-override"),
15_000
);
} finally {
rateLimitManager.refreshConnectionRateLimits("conn-zero-maxwait-override", null);
}
});
test("#6593 DEFAULT_REQUEST_QUEUE_MAX_DEPTH defaults to 0 (disabled) absent an env override", () => {
assert.equal(process.env.RATE_LIMIT_MAX_QUEUE_DEPTH, undefined);
assert.equal(resilienceSettings.DEFAULT_REQUEST_QUEUE_MAX_DEPTH, 0);

View File

@@ -66,9 +66,8 @@ test("422 'unknown variant xhigh, expected one of ...' clamps reasoning_effort a
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "xhigh");
assert.equal(capturedBodies[1].reasoning_effort, "high");
assert.equal(
getLearnedReasoningEffort("openai-compatible-chat-eaff6869", "qwen3-coder-30b-a3b-instruct"),
"high"
assert.ok(
(getLearnedReasoningEffort("openai-compatible-chat-eaff6869", "qwen3-coder-30b-a3b-instruct") as unknown as Set<string>).has("high")
);
assert.equal(result.response.status, 200);
} finally {
@@ -108,3 +107,125 @@ test("a second request for the same provider+model sends the learned value on th
globalThis.fetch = originalFetch;
}
});
test("400 please use low, high, or max clamps and retries once", async () => {
const executor = new SimpleExecutor();
const originalFetch = globalThis.fetch;
const capturedBodies: Record<string, unknown>[] = [];
const BODY_400_PLEASE_USE = JSON.stringify({
error: { message: "This model always engages in thinking and cannot be disabled; please use low, high, or max" },
});
globalThis.fetch = async (_url: string | URL | Request, init: RequestInit = {}) => {
const body = JSON.parse(String(init.body));
capturedBodies.push(body);
if (capturedBodies.length === 1) {
return new Response(BODY_400_PLEASE_USE, {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
try {
const result = await executor.execute({
model: "x-preview-f-free",
body: { reasoning_effort: "medium" },
stream: false,
credentials: {},
});
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "medium");
assert.equal(capturedBodies[1].reasoning_effort, "low");
const learned = getLearnedReasoningEffort("openai-compatible-chat-eaff6869", "x-preview-f-free") as unknown as Set<string>;
assert.ok(learned instanceof Set);
assert.ok(learned.has("low"));
assert.ok(learned.has("high"));
assert.equal(result.response.status, 200);
} finally {
globalThis.fetch = originalFetch;
}
});
test("400 please use low, medium with ultra retries to medium", async () => {
const executor = new SimpleExecutor();
const originalFetch = globalThis.fetch;
const capturedBodies: Record<string, unknown>[] = [];
const BODY_400_ULTRA = JSON.stringify({
error: { message: "please use low, medium" },
});
globalThis.fetch = async (_url: string | URL | Request, init: RequestInit = {}) => {
const body = JSON.parse(String(init.body));
capturedBodies.push(body);
if (capturedBodies.length === 1) {
return new Response(BODY_400_ULTRA, {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
try {
const result = await executor.execute({
model: "x-preview-f-free-2",
body: { reasoning_effort: "ultra" },
stream: false,
credentials: {},
});
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "ultra");
assert.equal(capturedBodies[1].reasoning_effort, "medium");
assert.equal(result.response.status, 200);
} finally {
globalThis.fetch = originalFetch;
}
});
test("no-op clamp does not retry: learned {high,max} with low request stays single-fetch", async () => {
const executor = new SimpleExecutor();
const originalFetch = globalThis.fetch;
const capturedBodies: Record<string, unknown>[] = [];
const BODY_400_HIGH_MAX = JSON.stringify({
error: { message: "please use high, or max" },
});
globalThis.fetch = async (_url: string | URL | Request, init: RequestInit = {}) => {
const body = JSON.parse(String(init.body));
capturedBodies.push(body);
if (capturedBodies.length === 1) {
return new Response(BODY_400_HIGH_MAX, {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
try {
// low is below the learned minimum {high,max}: downgrade-only passthrough,
// sanitizer leaves the body unchanged -> no identical-body retry.
const result = await executor.execute({
model: "x-preview-f-free-3",
body: { reasoning_effort: "low" },
stream: false,
credentials: {},
});
assert.equal(capturedBodies.length, 1);
assert.equal(capturedBodies[0].reasoning_effort, "low");
assert.equal(result.response.status, 400);
} finally {
globalThis.fetch = originalFetch;
}
});

View File

@@ -89,3 +89,67 @@ test("deepseek's non-ordinal max<->xhigh translation is untouched by the learned
// deepseek's special case returns early — xhigh -> max, never reaches the catch-all.
assert.equal(result.reasoning_effort, "max");
});
test("proactive clamp: medium→low for learned {low,high,max}", () => {
recordLearnedReasoningEffort("opencode-zen-direct", "x-preview-f-free", ["low", "high", "max"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "medium", model: "x-preview-f-free" },
"opencode-zen-direct",
"x-preview-f-free"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "low");
});
test("proactive clamp: xhigh→high for learned {low,high,max}", () => {
recordLearnedReasoningEffort("opencode-zen-direct", "x-preview-f-free-2", ["low", "high", "max"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "xhigh", model: "x-preview-f-free-2" },
"opencode-zen-direct",
"x-preview-f-free-2"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "high");
});
test("proactive clamp: ultra→max for learned {low,high,max}", () => {
recordLearnedReasoningEffort("opencode-zen-direct", "x-preview-f-free-3", ["low", "high", "max"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "ultra", model: "x-preview-f-free-3" },
"opencode-zen-direct",
"x-preview-f-free-3"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "max");
});
test("proactive clamp: ultra→medium for learned {low,medium}", () => {
recordLearnedReasoningEffort("acme", "m", ["low", "medium"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "ultra", model: "m" },
"acme",
"m"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "medium");
});
test("proactive clamp: high→medium for learned {low,medium}", () => {
recordLearnedReasoningEffort("acme", "m2", ["low", "medium"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "high", model: "m2" },
"acme",
"m2"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "medium");
});
test("no upgrade: low stays low for learned {high,max}", () => {
recordLearnedReasoningEffort("acme", "m3", ["high", "max"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "low", model: "m3" },
"acme",
"m3"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "low");
});
test("custom model ultra→medium for learned {low,medium}", () => {
recordLearnedReasoningEffort("openai-compatible-chat-eaff6869", "qwen3-coder-30b-a3b-instruct-2", ["low", "medium"]);
const out = sanitizeReasoningEffortForProvider(
{ reasoning_effort: "ultra", model: "qwen3-coder-30b-a3b-instruct-2" },
"openai-compatible-chat-eaff6869",
"qwen3-coder-30b-a3b-instruct-2"
) as { reasoning_effort: string };
assert.equal(out.reasoning_effort, "medium");
});

View File

@@ -5,6 +5,7 @@ import {
parseQuotaData,
hasFixedQuotaOrder,
} from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/quotaParsing";
import { resolveQuotaDisplayOrder } from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/parts/QuotaCardExpanded";
const quotaName = (quota: { name: string }) => quota.name;
@@ -62,3 +63,128 @@ test("#7764: providers WITHOUT a fixed order still sort worst-status-first (no r
const rendered = topQuotas(quotas, 3, "some-other-provider").map(quotaName);
assert.deepEqual(rendered, ["beta", "gamma", "alpha"]);
});
// ---------------------------------------------------------------------------
// #7764 residual: the original fix only whitelisted codex / GLM family / Kimi
// Coding in `hasFixedQuotaOrder`. Every OTHER provider that reports the same
// session + weekly rolling windows still gets re-sorted by remaining %, so two
// accounts of the SAME provider render the two bars in opposite positions
// depending on which window happens to be more depleted — the exact symptom in
// the report ("the indicators are not located in the same position per card").
//
// Quota names below are the real upstream keys, not simplified ones:
// claude → open-sse/services/usage/claude.ts:107,112 "session (5h)" / "weekly (7d)"
// minimax → open-sse/services/usage/minimax.ts:312,325 "session (5h)" / "weekly (7d)"
// zai → routed to getGlmUsage (open-sse/services/usage.ts:191-194)
// so it emits "5 Hours Quota" / "Weekly Quota" (glm.ts:33-34)
// command-code → open-sse/services/usage/command-code.ts:193,196 "five_hour" / "weekly"
// ---------------------------------------------------------------------------
/** Two refreshes of the same account family: in A the weekly window is the
* depleted one, in B it is the session window. A remaining-% sort flips the
* row order between the two; a canonical window order does not. */
function windowPair(sessionKey: string, weeklyKey: string) {
return {
depletedWeekly: {
quotas: {
[sessionKey]: { used: 9, total: 100, remainingPercentage: 91, resetAt: null },
[weeklyKey]: { used: 97, total: 100, remainingPercentage: 3, resetAt: null },
},
},
depletedSession: {
quotas: {
[sessionKey]: { used: 99, total: 100, remainingPercentage: 1, resetAt: null },
[weeklyKey]: { used: 43, total: 100, remainingPercentage: 57, resetAt: null },
},
},
};
}
const WINDOW_PROVIDERS: Array<{ provider: string; session: string; weekly: string }> = [
{ provider: "claude", session: "session (5h)", weekly: "weekly (7d)" },
{ provider: "minimax", session: "session (5h)", weekly: "weekly (7d)" },
{ provider: "minimax-cn", session: "session (5h)", weekly: "weekly (7d)" },
{ provider: "zai", session: "5 Hours Quota", weekly: "Weekly Quota" },
{ provider: "command-code", session: "five_hour", weekly: "weekly" },
];
for (const { provider, session, weekly } of WINDOW_PROVIDERS) {
test(`#7764 residual: ${provider} keeps session before weekly in the collapsed card across refreshes`, () => {
const { depletedWeekly, depletedSession } = windowPair(session, weekly);
const parsedA = parseQuotaData(provider, depletedWeekly);
const parsedB = parseQuotaData(provider, depletedSession);
// parseQuotaData already yields the canonical upstream order for both.
assert.deepEqual(parsedA.map(quotaName), [session, weekly]);
assert.deepEqual(parsedB.map(quotaName), [session, weekly]);
assert.deepEqual(
topQuotas(parsedA, 3, provider).map(quotaName),
[session, weekly],
`${provider}: collapsed card must not reorder rolling windows by remaining %`
);
assert.deepEqual(
topQuotas(parsedB, 3, provider).map(quotaName),
[session, weekly],
`${provider}: window order must be identical on the sibling account`
);
});
test(`#7764 residual: ${provider} expanded card window order matches the collapsed card`, () => {
const { depletedWeekly, depletedSession } = windowPair(session, weekly);
const parsedA = parseQuotaData(provider, depletedWeekly);
const parsedB = parseQuotaData(provider, depletedSession);
assert.deepEqual(resolveQuotaDisplayOrder(provider, parsedA).map(quotaName), [session, weekly]);
assert.deepEqual(resolveQuotaDisplayOrder(provider, parsedB).map(quotaName), [session, weekly]);
});
}
test("#7764 residual: a card whose quotas are NOT rolling windows still sorts worst-first", () => {
// Antigravity-style per-model buckets: no canonical chronological order
// exists, so the worst-status-first sort remains the useful one.
const parsed = parseQuotaData("antigravity", {
quotas: {
"gemini-3-pro": { used: 10, total: 100, remainingPercentage: 90 },
"gemini-3-flash": { used: 95, total: 100, remainingPercentage: 5 },
},
});
assert.deepEqual(topQuotas(parsed, 3, "antigravity").map(quotaName), [
"gemini-3-flash",
"gemini-3-pro",
]);
});
test("#7764 residual: a single rolling window plus credits is left to the remaining-% sort", () => {
// Only ONE window → no two windows to keep in a stable relative order, so
// nothing is claimed and the pre-existing behaviour is preserved.
const quotas = [
{ name: "credits", used: 0, total: 0, remainingPercentage: 90, isCredits: true },
{ name: "session (5h)", used: 95, total: 100, remainingPercentage: 5 },
];
assert.deepEqual(topQuotas(quotas, 3, "some-credit-provider").map(quotaName), [
"session (5h)",
"credits",
]);
});
test("#7764 residual: Claude per-model weekly windows keep upstream order and credits sink last", () => {
// Anthropic reports extra `weekly <model> (7d)` buckets plus an extra_usage
// credits row. The window sort must be STABLE: same-rank siblings keep the
// order parseQuotaData produced, and the credits row is not promoted.
const parsed = parseQuotaData("claude", {
quotas: {
"session (5h)": { used: 9, total: 100, remainingPercentage: 91 },
"weekly (7d)": { used: 97, total: 100, remainingPercentage: 3 },
"weekly designer (7d)": { used: 50, total: 100, remainingPercentage: 50 },
},
extraUsage: { is_enabled: true, monthly_limit: 100, used_credits: 10, utilization: 10 },
});
assert.deepEqual(topQuotas(parsed, 4, "claude").map(quotaName), [
"session (5h)",
"weekly (7d)",
"weekly designer (7d)",
"extra_usage",
]);
});

View File

@@ -12,7 +12,7 @@
* temp-directory filesystem.
*/
import { describe, it, beforeEach, after } from "node:test";
import { describe, it, beforeEach, after, mock } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
@@ -68,8 +68,11 @@ describe("resolveSpawnArgs (#6877 — real filesystem)", () => {
});
it("uses the .exe command name on Windows", async () => {
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
// resolveSpawnArgs reads os.platform() at call time (#11236 — a
// process.platform literal is constant-folded away by the Linux build of
// the published artifact), so the Windows host is simulated through the
// same runtime os.platform() seam binaryManager.test.ts uses for #10244.
const platformMock = mock.method(os, "platform", () => "win32");
try {
const { resolveSpawnArgs } =
@@ -78,9 +81,7 @@ describe("resolveSpawnArgs (#6877 — real filesystem)", () => {
assert.equal(result.command, path.join(dataDir, "bin", "cliproxyapi.exe"));
} finally {
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
}
platformMock.mock.restore();
}
});

View File

@@ -17,6 +17,7 @@ import {
parseLsofPid,
parseNetstatPid,
parseSsPid,
parseWindowsNetstatPid,
resolvePortPid,
} from "@/lib/services/portProbe";
@@ -65,8 +66,7 @@ test("parseNetstatPid matches on the local address, not the foreign one", () =>
});
test("parseNetstatPid reads macOS process:pid output", () => {
const stdout =
"tcp4 0 0 127.0.0.1.20128 *.* LISTEN 0 0 131072 131072 node:596922 00100\n";
const stdout = "tcp4 0 0 127.0.0.1.20128 *.* LISTEN 0 0 131072 131072 node:596922 00100\n";
assert.equal(parseNetstatPid(stdout, 20128), 596922);
});
@@ -77,6 +77,42 @@ test("parseNetstatPid ignores non-listening rows and unknown ports", () => {
assert.equal(parseNetstatPid("", 20128), null);
});
/**
* Realistic `netstat -ano` sample from Windows 11 (#11236 bug 6): the pid is
* the last whitespace-separated column and only exists on rows whose state is
* LISTENING. This is the only pid probe available on a stock Windows host —
* neither lsof nor ss nor net-tools `netstat -tlnp` exist there, so a Windows
* service adopted by the supervisor reported `pid: null` while healthy.
*/
const WINDOWS_NETSTAT_ANO = [
"Active Connections",
"",
" Proto Local Address Foreign Address State PID",
" TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1244",
" TCP 0.0.0.0:20128 0.0.0.0:0 LISTENING 12345",
" TCP 127.0.0.1:8317 0.0.0.0:0 LISTENING 5678",
" TCP 192.168.1.10:52413 140.82.121.4:443 ESTABLISHED 9012",
" TCP [::]:20128 [::]:0 LISTENING 12345",
" UDP 0.0.0.0:5353 *:* 3460",
"",
].join("\r\n");
test("parseWindowsNetstatPid reads the pid from a LISTENING row (#11236)", () => {
assert.equal(parseWindowsNetstatPid(WINDOWS_NETSTAT_ANO, 20128), 12345);
assert.equal(parseWindowsNetstatPid(WINDOWS_NETSTAT_ANO, 8317), 5678);
});
test("parseWindowsNetstatPid matches the local address, not the foreign one", () => {
// 443 appears only as a foreign address on an ESTABLISHED row.
assert.equal(parseWindowsNetstatPid(WINDOWS_NETSTAT_ANO, 443), null);
// 5353 appears only on a UDP row, which has no LISTENING state.
assert.equal(parseWindowsNetstatPid(WINDOWS_NETSTAT_ANO, 5353), null);
// A port that shares a suffix with a listening one must not match: 0128 vs
// 20128 — the `:` anchor on the local address prevents the partial hit.
assert.equal(parseWindowsNetstatPid(WINDOWS_NETSTAT_ANO, 128), null);
assert.equal(parseWindowsNetstatPid("", 20128), null);
});
test("resolvePortPid finds the pid holding a port", async () => {
const server = createServer();
await new Promise<void>((resolve) => server.listen(29994, "127.0.0.1", resolve));

View File

@@ -0,0 +1,98 @@
/**
* effort_tiers loop — learned set overrides synced metadata in catalog
* capabilities (design 2026-08-23, decisions: appris > sync, in-memory).
* Records go through the REAL record path (executor-style connection keys)
* then read back through the catalog builders — proves the key-space bridge,
* unlike a unit injection of the same string on both sides.
*/
import { test, after, beforeEach } from "node:test";
import assert from "node:assert/strict";
import {
recordLearnedReasoningEffort,
__test_resetLearnedReasoningEffortCaps,
} from "../../open-sse/services/learnedReasoningEffortCaps.ts";
import {
buildSyncedCapabilities,
mergeSyncedCapabilities,
} from "../../src/app/api/v1/models/syncedCapabilities.ts";
beforeEach(() => __test_resetLearnedReasoningEffortCaps());
after(() => __test_resetLearnedReasoningEffortCaps());
const SYNC_TIERS = ["none", "low", "medium", "high", "xhigh"];
test("learned set replaces synced effort_tiers", () => {
recordLearnedReasoningEffort("openai-compatible-chat-eaff6869", "x-preview-f-free", [
"low",
"high",
"max",
]);
const caps = buildSyncedCapabilities(
{ id: "x-preview-f-free", supportedThinkingEfforts: SYNC_TIERS },
"huggingface"
);
assert.deepEqual(caps?.effort_tiers, ["low", "high", "max"]);
});
test("nothing learned keeps synced metadata untouched", () => {
const caps = buildSyncedCapabilities(
{ id: "some-synced-model", supportedThinkingEfforts: SYNC_TIERS },
"huggingface"
);
assert.deepEqual(caps?.effort_tiers, SYNC_TIERS);
});
test("neither learned nor synced yields undefined", () => {
const caps = buildSyncedCapabilities({ id: "plain-model" }, "huggingface");
assert.equal(caps, undefined);
});
test("merge path keeps vision AND applies the learned override", () => {
recordLearnedReasoningEffort("conn-a", "vision-model", ["low", "max"]);
const merged = mergeSyncedCapabilities(
{ tool_calling: true },
{ id: "vision-model", supportsVision: true, supportedThinkingEfforts: SYNC_TIERS },
"huggingface"
);
assert.equal(merged?.vision, true);
assert.equal(merged?.tool_calling, true);
assert.deepEqual(merged?.effort_tiers, ["low", "max"]);
});
// Exclusion gate (#7694): codex/glm/kimi already own a conflicting
// `-{effort}` suffix mechanism — the blind opencode-plugin mapping must never
// see effort_tiers for them, learned or synced, or it double-handles the suffix.
for (const ownedBy of ["codex", "glm", "glm-cn", "glmt", "kimi", "kimi-coding-apikey"]) {
test(`build: excluded provider "${ownedBy}" never gets effort_tiers (synced)`, () => {
const caps = buildSyncedCapabilities(
{ id: "excluded-model", supportedThinkingEfforts: SYNC_TIERS },
ownedBy
);
assert.equal(caps?.effort_tiers, undefined);
});
test(`build: excluded provider "${ownedBy}" never gets effort_tiers (learned)`, () => {
recordLearnedReasoningEffort(`conn-${ownedBy}`, "excluded-model", ["low", "max"]);
const caps = buildSyncedCapabilities(
{ id: "excluded-model", supportedThinkingEfforts: SYNC_TIERS },
ownedBy
);
assert.equal(caps?.effort_tiers, undefined);
});
}
test("excluded provider still gets vision through buildSyncedCapabilities", () => {
const caps = buildSyncedCapabilities({ id: "codex-vision-model", supportsVision: true }, "codex");
assert.deepEqual(caps, { vision: true });
});
test("merge path also excludes codex/glm/kimi from effort_tiers", () => {
recordLearnedReasoningEffort("conn-glm", "glm-model", ["low", "max"]);
const merged = mergeSyncedCapabilities(
{ tool_calling: true },
{ id: "glm-model", supportsVision: true, supportedThinkingEfforts: SYNC_TIERS },
"glm"
);
assert.equal(merged?.vision, true);
assert.equal(merged?.effort_tiers, undefined);
});

View File

@@ -0,0 +1,80 @@
/**
* C1 — the `-<tier>` suffix resolver validates against the EFFECTIVE tier set
* (learned ?? sync), not raw synced metadata. Without this, the catalog
* advertises <alias>/<model>-max (learned set) but dispatch refuses to strip
* `-max` because sync metadata lacks the tier — dead-on-arrival variant.
* Harness mirrors deepseek-thinking-efforts.test.ts (custom provider +
* persistDiscoveredModels + async getModelInfo).
*/
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-c1-effort-dispatch-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "c1-test-secret";
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelDiscovery = await import("../../src/lib/providerModels/modelDiscovery.ts");
const { getModelInfo } = await import("../../src/sse/services/model.ts");
const { recordLearnedReasoningEffort, __test_resetLearnedReasoningEffortCaps } =
await import("@omniroute/open-sse/services/learnedReasoningEffortCaps.ts");
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
const PROVIDER = "c1prov";
const MODEL_ID = "c1-model";
async function seed() {
const connection = await providersDb.createProviderConnection({
provider: PROVIDER,
authType: "apikey",
name: "c1-runtime-efforts",
apiKey: `${PROVIDER}-key`,
isActive: true,
testStatus: "active",
});
// Sync tiers deliberately EXCLUDE max — only the learned set will vouch for it.
await modelDiscovery.persistDiscoveredModels(PROVIDER, connection.id, [
{ id: MODEL_ID, reasoning: { supported_efforts: ["none", "low", "medium", "high"] } },
]);
}
test.beforeEach(async () => {
__test_resetLearnedReasoningEffortCaps();
await resetStorage();
await seed();
});
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("-max resolves once the learned set advertises it (sync metadata does not)", async () => {
// Real record path, executor-style CONNECTION key — NOT the provider alias.
recordLearnedReasoningEffort("openai-compatible-chat-eaff6869", MODEL_ID, ["low", "high", "max"]);
const info = await getModelInfo(`${PROVIDER}/${MODEL_ID}-max`);
assert.equal(info.provider, PROVIDER);
assert.equal(info.model, MODEL_ID);
assert.equal(info.resolvedThinkingEffort, "max");
});
test("-medium still resolves via sync tiers even before anything is learned", async () => {
const info = await getModelInfo(`${PROVIDER}/${MODEL_ID}-medium`);
assert.equal(info.model, MODEL_ID);
assert.equal(info.resolvedThinkingEffort, "medium");
});
test("a tier neither learned nor synced is left untouched (literal id)", async () => {
recordLearnedReasoningEffort("conn-a", MODEL_ID, ["low"]);
const info = await getModelInfo(`${PROVIDER}/${MODEL_ID}-ultra`);
assert.equal(info.resolvedThinkingEffort, undefined);
});

View File

@@ -0,0 +1,168 @@
/**
* Structural regression guard for #11236 (Windows cliproxy residuals, bugs 2+3).
*
* Why this guard exists: the published npm artifact is bundled on Linux, and
* the bundler constant-folds every literal `process.platform` read to the
* BUILD machine's platform ("linux"), pruning the win32 branch from the
* shipped artifact. Precedent: b43a212680 (#10244/#10293), which converted
* detectPlatform/detectArch to runtime `os.platform()`/`os.arch()` reads for
* exactly this reason. #10371 later fixed the Windows `.exe` binary name in
* the source but left literal `process.platform` reads behind in the same
* runtime paths, so the shipped artifact still:
* - named the managed binary `cliproxyapi` (no `.exe`) at install time
* (binaryManager.managedBinaryName), and
* - spawned that extension-less path at start time
* (installers/cliproxy.resolveSpawnArgs) -> ENOENT on Windows even with a
* valid `.exe` in place (issue #11236 bugs 2 and 3).
*
* The runtime-safe pattern is a call-time `os.platform()` read. This guard
* fails if `process.platform` reappears outside a comment in any file whose
* platform branch feeds the published artifact's runtime behavior (binary
* name, spawn path, per-OS probe selection).
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
const GUARDED_FILES = [
"src/lib/versionManager/binaryManager.ts",
"src/lib/versionManager/processManager.ts",
"src/lib/services/installers/cliproxy.ts",
"src/lib/services/portProbe.ts",
];
interface Offender {
line: number;
text: string;
}
/**
* Returns the source with every `//` and `/* ... *\/` comment blanked out
* (replaced by spaces, newlines preserved so line numbers are stable). String
* literals are kept verbatim — a `process.platform` inside one is still
* flagged, which is acceptable: none of the guarded files carry the pattern
* in a string, and a false positive there is safer than a false negative in
* code.
*/
function stripComments(source: string): string {
let out = "";
let i = 0;
let inBlock = false;
let inLine = false;
let inString: string | null = null;
while (i < source.length) {
const ch = source[i];
const next = source[i + 1];
if (inLine) {
if (ch === "\n") {
inLine = false;
out += ch;
} else {
out += " ";
}
i++;
continue;
}
if (inBlock) {
if (ch === "*" && next === "/") {
inBlock = false;
out += " ";
i += 2;
continue;
}
out += ch === "\n" ? "\n" : " ";
i++;
continue;
}
if (inString) {
out += ch;
if (ch === "\\") {
out += next ?? "";
i += 2;
continue;
}
if (ch === inString) inString = null;
i++;
continue;
}
if (ch === "/" && next === "/") {
inLine = true;
out += " ";
i += 2;
continue;
}
if (ch === "/" && next === "*") {
inBlock = true;
out += " ";
i += 2;
continue;
}
if (ch === '"' || ch === "'" || ch === "`") inString = ch;
out += ch;
i++;
}
return out;
}
/**
* Every remaining `process.platform` occurrence after comment stripping is an
* offender — the fold-explanation comments reference the pattern by name and
* must remain free to do so.
*/
function findFoldableReads(source: string): Offender[] {
const stripped = stripComments(source);
const offenders: Offender[] = [];
stripped.split("\n").forEach((line, index) => {
if (line.includes("process.platform")) {
offenders.push({ line: index + 1, text: source.split("\n")[index].trim() });
}
});
return offenders;
}
for (const relPath of GUARDED_FILES) {
test(`${relPath} has no build-foldable process.platform reads (#11236)`, () => {
const source = fs.readFileSync(path.join(REPO_ROOT, relPath), "utf8");
const offenders = findFoldableReads(source);
assert.deepEqual(
offenders,
[],
`${relPath} must read os.platform() at call time instead of the ` +
`build-foldable process.platform literal (Turbopack folds it to the ` +
`Linux build machine — b43a212680 / #10244 / #10371). Offenders: ` +
offenders.map((o) => `L${o.line}: ${o.text}`).join("; ")
);
});
}
// Guard-the-guard (mutation check on synthetic input, so the real sources
// never need to be touched): a code occurrence MUST be caught, comment-only
// occurrences MUST be let through.
test("findFoldableReads catches a code occurrence (mutation self-check)", () => {
const snippet = [
'const name = process.platform === "win32" ? "a.exe" : "a";',
"// process.platform in a line comment is allowed",
"/**",
" * process.platform in a block comment is allowed",
" */",
"/* process.platform single-line block is allowed */",
"const ok = os.platform();",
].join("\n");
const offenders = findFoldableReads(snippet);
assert.equal(offenders.length, 1);
assert.equal(offenders[0].line, 1);
});
test("findFoldableReads reports nothing when only comments mention the pattern", () => {
const snippet = [
"// process.platform",
"/* process.platform */",
"const p = os.platform();",
].join("\n");
assert.deepEqual(findFoldableReads(snippet), []);
});