diff --git a/changelog.d/maintenance/basereds-v3851-0917.md b/changelog.d/maintenance/basereds-v3851-0917.md
new file mode 100644
index 0000000000..d0ce6df04c
--- /dev/null
+++ b/changelog.d/maintenance/basereds-v3851-0917.md
@@ -0,0 +1 @@
+- **chore(quality):** clear the `release/v3.8.51` base-reds — 19 failing unit tests plus the `API Route Typecheck` and `mutation-test-coverage` gates. Three fixtures still built `*-compatible-*` connections with no `baseUrl` and so tripped the #13452/#13798 guard that now refuses to fall back to the real OpenAI/Anthropic API; `modelDiscovery.ts` missed the `VertexModelMetadataProvenance` cast its read-path twin already had (#12471); a raw NUL byte in a provider-test regexp made git treat the file as binary; and the reserved-prefix count, the budget-card SVG and the Stryker `tap.testFiles` list had drifted. The #2331, OAuth-loopback and i18n guards were re-expressed as the invariants they protect — each re-verified by mutating the source back and watching it fail.
diff --git a/config/quality/dependency-allowlist.json b/config/quality/dependency-allowlist.json
index 21baa98694..84e048a7fe 100644
--- a/config/quality/dependency-allowlist.json
+++ b/config/quality/dependency-allowlist.json
@@ -4,7 +4,8 @@
"@testing-library/dom": "Peer dep obrigatoria de @testing-library/react v16 (adicionada no PR #11224); Refs #9985.",
"@testing-library/user-event": "Utilitario oficial do ecossistema testing-library para testes de UI (adicionada no PR #11224); Refs #9985.",
"babel-plugin-react-compiler": "Official React Compiler Babel plugin (facebook/react, MIT). Required peer of Next.js 16 `reactCompiler: true`; Next declares it optional (`*`) and does not auto-install. Added by PR #11783 / issue #67.",
- "eslint-plugin-react-hooks": "React Hooks lint rules (set-state-in-effect, immutability, refs, purity) pinned at 7.0.1 by the release/v3.8.51 cycle; the 224 findings it raised are tracked in #11924. Refs #11924."
+ "eslint-plugin-react-hooks": "React Hooks lint rules (set-state-in-effect, immutability, refs, purity) pinned at 7.0.1 by the release/v3.8.51 cycle; the 224 findings it raised are tracked in #11924. Refs #11924.",
+ "vite": ""
},
"allowed": [
"@atjsh/llmlingua-2",
@@ -151,6 +152,7 @@
"undici",
"update-notifier",
"uuid",
+ "vite",
"vitest",
"wait-on",
"wreq-js",
diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json
index 872d6116ca..994a212281 100644
--- a/config/quality/file-size-baseline.json
+++ b/config/quality/file-size-baseline.json
@@ -234,6 +234,7 @@
"cap": 1200,
"testCap": 1200,
"testFrozen": {
+ "_rebaseline_2026_09_17_13947_basereds_drain": "Base-red drain da PR #13947 (Refs #13866). tests/unit/sse-auth.test.ts 1729->1733 (+4): o guard #12080 afirmava `assert.equal(selected, null)` e passou a receber o diagnostico de key-policy que a #13879 introduziu — mesma forma de sentinela que o caminho de estado terminal usa desde #12441. A asserção foi reescrita para a restrição que a #12080 protege (deepEqual exato: nada utilizavel volta, nenhuma conexao vaza) mais o comentario que explica por que o null sumiu; as tres asserções intermediarias foram removidas por serem redundantes com o deepEqual estrito, entao o crescimento liquido e +3 e nao +8. tests/unit/batch_api.test.ts 1348->1353 (+5): NAO e desta PR — chegou nos 13 commits que avancaram o tip durante o trabalho e nunca foi rebaselinado, deixando o cap abaixo do estado mergeado. Registrado aqui contra o estado merged; nenhum outro cap e tocado.",
"_rebaseline_2026_06_27_5193_antigravity_test": "#5193 own test growth: oauth-providers-config.test.ts 870->873 (+3: antigravity projectId assertion + 50ms tick for the now fire-and-forget onboarding, matching the no-PKCE/no-openid flow).",
"_rebaseline_2026_07_02_5928_base_red": "web-cookie-providers-new.test.ts 845->850: #5928 (test(security) Kimi Web URL host parse, CodeQL #689) grew the file +5 lines and merged into release/v3.8.44 WITHOUT rebaselining, leaving a fast-gates base-red that blocked every subsequent PR->release. Test growth is legitimate (a security regression test); maintainer absorbs the drift here. Frozen at 850.",
"_rebaseline_2026_07_09_6126_clinepass_dualauth": "#6126 (ClinePass dual-auth) own test growth: oauth-providers-config.test.ts 842->845 (+3: clinepass key/config/required-fields entries reusing the Cline WorkOS flow config, needed after registering clinepass in the oauth.ts PROVIDERS enum).",
@@ -249,7 +250,7 @@
"_rebaseline_pr4613_compatible_provider_groups": "Reconcile #4613 already-merged growth: providers-page-utils.test.ts 1004->1052 (+48, buildCompatibleProviderGroups partition unit test). Fast-gate PR->release does not run check:file-size, so this surfaced post-merge.",
"tests/integration/chat-pipeline.test.ts": 1736,
"tests/unit/account-fallback-service.test.ts": 2056,
- "tests/unit/batch_api.test.ts": 1348,
+ "tests/unit/batch_api.test.ts": 1353,
"tests/unit/cc-compatible-provider.test.ts": 1225,
"tests/unit/chatcore-translation-paths.test.ts": 3447,
"tests/unit/chatgpt-web.test.ts": 4911,
@@ -265,7 +266,7 @@
"tests/unit/provider-validation-specialty.test.ts": 2912,
"tests/unit/reasoning-cache.test.ts": 1291,
"tests/unit/route-edge-coverage.test.ts": 1244,
- "tests/unit/sse-auth.test.ts": 1729,
+ "tests/unit/sse-auth.test.ts": 1733,
"tests/unit/stream-utils.test.ts": 2517,
"tests/unit/token-refresh-service.test.ts": 1407,
"tests/unit/translator-openai-responses-req.test.ts": 1470,
@@ -354,7 +355,8 @@
"_rebaseline_2026_07_27_3850_relax_filesize_cap": "OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). cap 800->900 (+100), testCap 800->900 (+100). Targets: decompose-existing-frozen unchanged (frozen still only-shrink); this only relaxes the cap for NEW files in the decompose/extract-while-PREPARE phase (.51='executor registry in-place' and .52='combo.ts decomposition' create new leaf modules above 800). RE-TIGHTENING MANDATORY in v3.8.51: cap target 850 = 850 once decomposition wave stabilizes. SUPERSEDED by _rebaseline_2026_07_27_3850_relax_filesize_cap_v2_20pct (v1 +20% buffer) — retained for audit. Tracked via same roadmap issue.",
"_rebaseline_2026_07_27_v3849_train1h": "Merge-train 1H (31 PRs) — owner-approved 2026-07-27. Two distinct causes, kept separate on purpose: (1) GENUINE irreducible growth at existing chokepoints — providerLimits/auth (#8632 Kimi quota-reset recovery), rateLimitManager (#8616 idle wedged limiters), models-catalog-route.test (#8610 OpenCode Go effort aliases); (2) COLLISION with #8585, which banked shrinks measured on the pre-train release tip while 30 sibling PRs in the SAME train grew those files again — chat/accountFallback (#8628), chatCore (#8613), videoGeneration (#8581), imageGeneration. The zero-headroom frozen entries cannot absorb either. Ceilings re-pinned to the post-merge tip; #8612 (also in this train) automates shrink-banking so this self-inflicted drift stops recurring. Detail: src/lib/usage/providerLimits.ts 1006->1013 (#8632); src/sse/services/auth.ts 2492->2508 (#8632); open-sse/services/rateLimitManager.ts 1014->1060 (#8616); src/sse/handlers/chat.ts 1842->1845 (#8628); open-sse/handlers/chatCore.ts 4939->4955 (#8613); open-sse/handlers/imageGeneration.ts 3100->3101 ((sem PR — teto do #8585)); open-sse/handlers/videoGeneration.ts 1038->1063 (#8581); open-sse/services/accountFallback.ts 1965->1966 (#8628); tests/unit/models-catalog-route.test.ts 1608->1636 (#8610)",
"frozen": {
- "src/sse/handlers/chatHelpers.ts": 1231,
+ "_rebaseline_2026_09_17_13947_tip_growth": "Base-red drain da PR #13947 (Refs #13866) — crescimento de PRODUCAO que chegou pelo tip e nunca foi rebaselinado; nenhum destes arquivos e tocado por esta PR. #12906 (d70f43d4, retry empty_response 502 + timeout de inicio de resposta ciente de reasoning): src/sse/handlers/chat.ts 2498->2500, src/sse/handlers/chatHelpers.ts 1231->1245, open-sse/utils/proxyFetch.ts 1275->1276, open-sse/utils/stream.ts 3098->3123. #12904 (f3acf4f8, injecao unica do system prompt global pos-traducao) + #12910 (051576fd, finalizacao de cache semantico por request id exato): open-sse/handlers/chatCore.ts 6181->6203. Anteriores ao lote, ja acima do cap na base 3d5baf13: open-sse/handlers/imageGeneration.ts 3293->3304 (#13748, b97338a8) e open-sse/services/combo/roundRobinCombo.ts 1213->1221 (#13776, aeba6b1a). Registrado contra o estado mergeado; nenhum outro cap e tocado.",
+ "src/sse/handlers/chatHelpers.ts": 1245,
"_rebaseline_2026_09_15_13609_mistral_ambiguous_401": "PR #13609 rework (maxmad64bis, bare Mistral 401 soft lockout behind MISTRAL_AMBIGUOUS_401_SOFT_LOCKOUT, default off). open-sse/services/accountFallback.ts 2469->2501 (+32): +14 are the change itself (shared-predicate + flag imports, the documented ambiguousAuth field on the checkFallbackError return type, and the flag-gated 401 branch formatted normally instead of the PR's 139-char squeezed configuredRule line); +18 are the lint-staged prettier pass normalizing lines that were already unformatted on the release tip (multi-import, ISO_RETRY_RE, two regex arrays, persistAntigravityFamilyCooldownIfQuota call, applyErrorState guard, trailing commas) — pure formatting, no logic. src/sse/services/auth.ts 3556->3557 (+1): markAccountUnavailable passes connectionId to resolveTerminalConnectionStatus so the soft-strike bound is per connection. The predicate and strike tracker live in the leaf open-sse/services/accountFallback/mistralAmbiguousAuth.ts (under cap). Covered by tests/unit/provider-401-ambiguous-runtime.test.ts (flag off/on, end-to-end through markAccountUnavailable).",
"_rebaseline_2026_06_22_4644_deepseek_web_tools": "PR #4644 (BugsBag/robust deepseek-web tool-call parsing): open-sse/executors/deepseek-web.ts 1117->1125 (+8). The new agentic tool-call path emits surrounding text + reasoning before tool_calls and swaps to the dedicated deepseekWebTools.ts parser; the +8 lines are cohesive wiring at the existing transformSSE chokepoint (the parser itself lives in the new deepseekWebTools.ts file, already under cap). The PR's own fast-gate (PR->release) does not run check:file-size, so this surfaced only at release reconcile. Covered by tests/unit/deepseek-web-tools-variants.test.ts + deepseek-web-tools-execute.test.ts.",
"_rebaseline_2026_06_23_4712_deepseek_web_tool_results": "PR for #4712 (deepseek-web drops role:tool): open-sse/executors/deepseek-web.ts 1125->1148 (+23). messagesToPrompt() now folds role:\"tool\" results into the single-prompt transcript (recovering the tool name from the preceding assistant tool_calls by tool_call_id) instead of silently dropping them; the lines are cohesive wiring inside the existing function. Covered by tests/unit/deepseek-web-tool-result-prompt-4712.test.ts.",
@@ -462,8 +464,8 @@
"open-sse/executors/codex.ts": 1528,
"open-sse/executors/cursor.ts": 1847,
"open-sse/executors/muse-spark-web.ts": 1405,
- "open-sse/handlers/chatCore.ts": 6181,
- "open-sse/handlers/imageGeneration.ts": 3293,
+ "open-sse/handlers/chatCore.ts": 6203,
+ "open-sse/handlers/imageGeneration.ts": 3304,
"open-sse/handlers/search.ts": 1789,
"open-sse/mcp-server/schemas/tools.ts": 1621,
"open-sse/mcp-server/server.ts": 1572,
@@ -473,8 +475,8 @@
"open-sse/services/combo/executeTargetAttempt.ts": 1228,
"open-sse/translator/response/openai-responses.ts": 1466,
"open-sse/utils/cursorAgentProtobuf.ts": 1547,
- "open-sse/utils/proxyFetch.ts": 1275,
- "open-sse/utils/stream.ts": 3098,
+ "open-sse/utils/proxyFetch.ts": 1276,
+ "open-sse/utils/stream.ts": 3123,
"open-sse/vendor/codex-chatgpt-web/adapters/chatgpt-web/browser-worker.ts": 4398,
"open-sse/vendor/codex-chatgpt-web/bridge.ts": 1335,
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 1344,
@@ -502,12 +504,12 @@
"src/shared/components/RequestLoggerV2.tsx": 1718,
"src/shared/constants/providers/apikey/gateways.ts": 1502,
"src/shared/services/cliRuntime.ts": 1296,
- "src/sse/handlers/chat.ts": 2498,
+ "src/sse/handlers/chat.ts": 2500,
"src/sse/services/auth.ts": 3557,
"tests/unit/account-fallback-service.test.ts": 2453,
"tests/unit/provider-validation-specialty.test.ts": 4656,
"open-sse/services/autoCombo/virtualFactory.ts": 1230,
- "open-sse/services/combo/roundRobinCombo.ts": 1213
+ "open-sse/services/combo/roundRobinCombo.ts": 1221
},
"_rebaseline_2026_09_15_roundrobin_dashboard_events": "Fix #13089 (Combo Studio Live dashboard shows an empty backlog for round-robin combos): open-sse/services/combo/roundRobinCombo.ts 1205->1213. Round-robin is the only combo strategy that bypasses handleComboChat/executeTargetAttempt.ts, the path that publishes the combo.target.attempt/succeeded/failed EventBus events the Live dashboard listens for — so round-robin completions never showed up. The new call-site wiring (createRRDashboardEvents(...) instantiated once per target, one-line .attempt()/.succeeded()/.failed() calls at the 6 existing dispatch/outcome points) is the emitter logic actually extracted into a new module, open-sse/services/combo/rrDashboardEvents.ts — this is the minimum irreducible footprint for wiring 6 required call sites into 6 fixed control-flow points of the frozen file. Covered by tests/unit/issue-13089-roundrobin-live-ws-events.test.ts (2 tests: success + failure paths).",
"_rebaseline_base_2026_08_10_proxyfetch": "Base-red fix (green-prs sweep, issue #9985): open-sse/utils/proxyFetch.ts 1207 > cap 1000 — new proxied-TLS fetch helper introduced by the Fal reference-image work. Owner-authorized quick rebaseline to green; structural slim tracked for v3.9.0.",
diff --git a/docs/screenshots/free-tier-budget-card.svg b/docs/screenshots/free-tier-budget-card.svg
index d9c98f7cfb..5c47cdf7ac 100644
--- a/docs/screenshots/free-tier-budget-card.svg
+++ b/docs/screenshots/free-tier-budget-card.svg
@@ -3,7 +3,7 @@
OmniRoute · /dashboard/free-tiers · preview mockup
Monthly free-token budget
-21 free pools · 443 models · one endpoint
+21 free pools · 452 models · one endpoint
Steady / month
~1.47B
First month (+ signup credits)
@@ -99,6 +99,6 @@
hyperbolic 5M
Pool-deduped, honest counting — no inflated rate-limit ceilings. Some terms suggest personal-use only; we flag them so you decide.
-+ 15 permanently-free, no-cap providers (e.g. agnes, ainative, aion) · OpenRouter $10 → +24M/mo.
++ 16 permanently-free, no-cap providers (e.g. agnes, agnes-cn, ainative) · OpenRouter $10 → +24M/mo.
+ ~6M behind regional identity verification (modelscope) — real quota, never in the headline.
diff --git a/open-sse/translator/request/openai-responses.ts b/open-sse/translator/request/openai-responses.ts
index e51851f006..697f6371b4 100644
--- a/open-sse/translator/request/openai-responses.ts
+++ b/open-sse/translator/request/openai-responses.ts
@@ -82,24 +82,30 @@ function appendReasoningContent(current: unknown, next: string): string {
function normalizeRoleBasedToolCalls(toolCalls: unknown): JsonRecord[] {
if (!Array.isArray(toolCalls)) return [];
- return toolCalls
- .map((toolCallValue) => {
- const toolCall = toRecord(toolCallValue);
- const fn = toRecord(toolCall.function);
- const name = toString(fn.name).trim();
- const id = toString(toolCall.id).trim();
- if (!name || !id) return null;
- return {
- id,
- type: "function",
- function: {
- name,
- arguments:
- typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {}),
- },
- };
- })
- .filter((toolCall): toolCall is JsonRecord => toolCall !== null);
+ return (
+ toolCalls
+ .map((toolCallValue) => {
+ const toolCall = toRecord(toolCallValue);
+ const fn = toRecord(toolCall.function);
+ const name = toString(fn.name).trim();
+ const id = toString(toolCall.id).trim();
+ if (!name || !id) return null;
+ return {
+ id,
+ type: "function",
+ function: {
+ name,
+ arguments:
+ typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {}),
+ },
+ };
+ })
+ // The mapped element is the tool-call object or null, which is NOT a
+ // Record as far as the predicate rule is concerned (TS2677:
+ // the predicate type must be assignable to the parameter type). Narrow by the
+ // element's own type; the literal satisfies JsonRecord at the return.
+ .filter((toolCall): toolCall is NonNullable => toolCall !== null)
+ );
}
/**
diff --git a/package-lock.json b/package-lock.json
index 3399dc2688..ceb21949f6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -157,6 +157,7 @@
"type-coverage": "^2.30.1",
"typescript": "^6.0.3",
"typescript-eslint": "^8.70.0",
+ "vite": "^8.0.16",
"vitest": "^5.0.0",
"wait-on": "^9.1.0",
"wtfnode": "^0.10.1"
@@ -9706,6 +9707,16 @@
"node": "^20.19.0 || >=22.12.0"
}
},
+ "node_modules/@oxc-project/types": {
+ "version": "0.150.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.150.0.tgz",
+ "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/oxc-project"
+ }
+ },
"node_modules/@oxc-resolver/binding-android-arm-eabi": {
"version": "11.24.2",
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz",
@@ -11579,6 +11590,279 @@
}
}
},
+ "node_modules/@rolldown/binding-android-arm-eabi": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz",
+ "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz",
+ "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz",
+ "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz",
+ "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz",
+ "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz",
+ "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz",
+ "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz",
+ "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz",
+ "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz",
+ "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz",
+ "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz",
+ "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz",
+ "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz",
+ "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz",
+ "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
@@ -35089,6 +35373,40 @@
"integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
"license": "Unlicense"
},
+ "node_modules/rolldown": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.9.tgz",
+ "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.150.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm-eabi": "1.2.9",
+ "@rolldown/binding-android-arm64": "1.2.9",
+ "@rolldown/binding-darwin-arm64": "1.2.9",
+ "@rolldown/binding-darwin-x64": "1.2.9",
+ "@rolldown/binding-freebsd-x64": "1.2.9",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.9",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.9",
+ "@rolldown/binding-linux-arm64-musl": "1.2.9",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.9",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.9",
+ "@rolldown/binding-linux-x64-gnu": "1.2.9",
+ "@rolldown/binding-linux-x64-musl": "1.2.9",
+ "@rolldown/binding-openharmony-arm64": "1.2.9",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.9",
+ "@rolldown/binding-win32-x64-msvc": "1.2.9"
+ }
+ },
"node_modules/roughjs": {
"version": "4.6.6",
"resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",
@@ -38603,6 +38921,372 @@
"d3-timer": "^3.0.1"
}
},
+ "node_modules/vite": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz",
+ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.33.0",
+ "picomatch": "^4.0.7",
+ "postcss": "^8.5.28",
+ "rolldown": "~1.2.6",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.7.1",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-android-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-darwin-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-darwin-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-freebsd-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
"node_modules/vitest": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz",
diff --git a/package.json b/package.json
index 2bac9e2242..d448055847 100644
--- a/package.json
+++ b/package.json
@@ -440,6 +440,7 @@
"type-coverage": "^2.30.1",
"typescript": "^6.0.3",
"typescript-eslint": "^8.70.0",
+ "vite": "^8.0.16",
"vitest": "^5.0.0",
"wait-on": "^9.1.0",
"wtfnode": "^0.10.1"
diff --git a/src/app/api/providers/[id]/test/route.ts b/src/app/api/providers/[id]/test/route.ts
index 7d72f28098..3b01d1836a 100644
--- a/src/app/api/providers/[id]/test/route.ts
+++ b/src/app/api/providers/[id]/test/route.ts
@@ -244,7 +244,7 @@ function isTokenExpired(connection: any) {
// token — but is capped and stripped of control characters defensively before it reaches
// the stored/surfaced error message, per docs/security/ERROR_SANITIZATION.md.
function sanitizeUpstreamBodyText(bodyText: string): string {
- const collapsed = bodyText.replace(/[\r\n\t -]+/g, " ").trim();
+ const collapsed = bodyText.replace(/[\r\n\t\u0000-\u001f]+/g, " ").trim();
const MAX_LENGTH = 300;
return collapsed.length > MAX_LENGTH ? `${collapsed.slice(0, MAX_LENGTH)}…` : collapsed;
}
diff --git a/src/lib/providerModels/modelDiscovery.ts b/src/lib/providerModels/modelDiscovery.ts
index 4e5cf1c300..bfe9131cf4 100644
--- a/src/lib/providerModels/modelDiscovery.ts
+++ b/src/lib/providerModels/modelDiscovery.ts
@@ -4,6 +4,7 @@ import {
replaceSyncedAvailableModelsForConnection,
type SyncedAvailableModel,
} from "@/lib/db/models";
+import type { VertexModelMetadataProvenance } from "@/lib/providerModels/vertexModelMetadata";
import { CANONICAL_EFFORT_VALUES } from "@/shared/reasoning/effortStandardization";
import { isObsoleteKiroModelAlias } from "@omniroute/open-sse/services/kiroModels.ts";
import { filterSelectableModels } from "@omniroute/open-sse/services/modelLifecycle.ts";
@@ -352,8 +353,11 @@ export function normalizeDiscoveredModels(
...(typeof inputTokenLimit === "number" ? { inputTokenLimit } : {}),
...(isVertexProvider && typeof contextWindow === "number" ? { contextWindow } : {}),
...(typeof outputTokenLimit === "number" ? { outputTokenLimit } : {}),
+ // The narrowed `object` is not assignable to VertexModelMetadataProvenance; the
+ // read path (src/lib/db/models/synced.ts) casts the same field the same way, so
+ // keep both sides of the round-trip identical rather than only one of them typed.
...(record.metadataProvenance && typeof record.metadataProvenance === "object"
- ? { metadataProvenance: record.metadataProvenance }
+ ? { metadataProvenance: record.metadataProvenance as VertexModelMetadataProvenance }
: {}),
...(typeof record.description === "string" ? { description: record.description } : {}),
...(typeof record.supportsThinking === "boolean"
diff --git a/stryker.conf.json b/stryker.conf.json
index 372f473b37..eb10733040 100644
--- a/stryker.conf.json
+++ b/stryker.conf.json
@@ -469,7 +469,10 @@
"tests/unit/issue-11912-opencode-roundrobin-collapse.test.ts",
"tests/unit/opencode-400-model-unavailable.test.ts",
"tests/unit/strong-credential-token-linear-scan.test.ts",
- "tests/unit/provider-401-ambiguous-runtime.test.ts"
+ "tests/unit/provider-401-ambiguous-runtime.test.ts",
+ "tests/unit/13601-header-drop-count-surfaced.test.ts",
+ "tests/unit/combo-identical-error-streak.test.ts",
+ "tests/unit/semantic-cache-no-truncated-writes.test.ts"
],
"nodeArgs": [
"--import",
diff --git a/tests/unit/authz/pipeline.test.ts b/tests/unit/authz/pipeline.test.ts
index 88cbc50680..441da1db50 100644
--- a/tests/unit/authz/pipeline.test.ts
+++ b/tests/unit/authz/pipeline.test.ts
@@ -371,18 +371,37 @@ test("runAuthzPipeline allows dashboard sessions to read model catalog aliases",
assert.equal(response.headers.get("x-omniroute-route-class"), "CLIENT_API");
});
-test("runAuthzPipeline allows dashboard sessions to reach DB health management API", async () => {
+test("runAuthzPipeline gates the DB health API on loopback, not on the session alone", async () => {
await forceAuthRequired();
- const response = await pipeline.runAuthzPipeline(
+ // #13717 moved /api/db/health to Tier 1 LOCAL_ONLY: runManagedDbHealthCheck()
+ // forks native diagnostics into a child process (Hard Rules #15 + #17), and the
+ // route is NOT in LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES. So a dashboard session
+ // is no longer sufficient by itself — an unstamped peer fails closed, whatever the
+ // URL says, because requestPeerAddress() never reads the spoofable Host header.
+ const unstamped = await pipeline.runAuthzPipeline(
request("http://localhost/api/db/health", {
headers: { cookie: await dashboardCookie() },
}),
{ enforce: true }
);
+ assert.equal(unstamped.status, 403);
- assert.equal(response.status, 200);
- assert.equal(response.headers.get("x-omniroute-route-class"), "MANAGEMENT");
+ // The local operator — real TCP peer 127.0.0.1, stamped by the custom server —
+ // still reaches it with their session.
+ process.env.OMNIROUTE_PEER_STAMP_TOKEN = "pipeline-test-peer-stamp-token";
+ const loopback = await pipeline.runAuthzPipeline(
+ request("http://localhost/api/db/health", {
+ headers: {
+ cookie: await dashboardCookie(),
+ "x-omniroute-peer-ip": "pipeline-test-peer-stamp-token|127.0.0.1",
+ "x-omniroute-via-proxy": "pipeline-test-peer-stamp-token|0",
+ },
+ }),
+ { enforce: true }
+ );
+ assert.equal(loopback.status, 200);
+ assert.equal(loopback.headers.get("x-omniroute-route-class"), "MANAGEMENT");
});
test("runAuthzPipeline accepts dashboard mutations from configured public origin", async () => {
diff --git a/tests/unit/chatcore-translation-paths.test.ts b/tests/unit/chatcore-translation-paths.test.ts
index c0125fff83..613480d840 100644
--- a/tests/unit/chatcore-translation-paths.test.ts
+++ b/tests/unit/chatcore-translation-paths.test.ts
@@ -411,7 +411,13 @@ async function invokeChatCore({
modelInfo: { provider, model, extendedContext: false },
credentials: credentials || {
apiKey: "sk-test",
- providerSpecificData: {},
+ // #13452/#13798: buildUrl() refuses an `*-compatible-*` node with no baseUrl
+ // rather than defaulting to the real OpenAI/Anthropic API, so the default
+ // fixture has to hydrate the connection the way a configured one is. Real
+ // providers keep the empty bag — their URL comes from the registry.
+ providerSpecificData: /-compatible-/.test(provider)
+ ? { baseUrl: "https://compatible.example/v1" }
+ : {},
},
log: noopLog(),
clientRawRequest: {
diff --git a/tests/unit/codex-effort-alias-priority.test.ts b/tests/unit/codex-effort-alias-priority.test.ts
index c2e4f840d7..10e1417c51 100644
--- a/tests/unit/codex-effort-alias-priority.test.ts
+++ b/tests/unit/codex-effort-alias-priority.test.ts
@@ -93,7 +93,7 @@ import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const CODEX_SRC = path.resolve(__dirname, "../../open-sse/executors/codex.ts");
-test("#2331 codex.ts still prioritizes modelEffort first in rawEffort chain", () => {
+test("#2331 codex.ts still ranks modelEffort above client-injected reasoning defaults", () => {
const src = fs.readFileSync(CODEX_SRC, "utf8");
// The chain we expect: rawEffort = modelEffort || explicitReasoning || ...
@@ -104,10 +104,20 @@ test("#2331 codex.ts still prioritizes modelEffort first in rawEffort chain", ()
assert.ok(match, "rawEffort assignment not found in codex.ts");
const chain = match![1].replace(/\s+/g, " ").trim();
- const firstToken = chain.split("||")[0].trim();
- assert.equal(
- firstToken,
- "modelEffort",
- `rawEffort priority chain must start with modelEffort, got: ${chain}`
+ const order = chain.split("||").map((token) => token.trim());
+ const at = (token: string) => order.indexOf(token);
+
+ // #2331's invariant is a RELATIVE one: a model-suffix alias (gpt-5.5-xhigh) must beat
+ // the defaults a client injects (OpenCode's reasoning.effort=medium, reasoning_effort).
+ // It is not a claim about the head of the chain — #13556 deliberately put the
+ // server-selected force rule ahead of everything, which is stronger than both.
+ assert.ok(at("modelEffort") >= 0, `modelEffort missing from chain: ${chain}`);
+ assert.ok(
+ at("modelEffort") < at("explicitReasoning"),
+ `modelEffort must outrank explicitReasoning, got: ${chain}`
+ );
+ assert.ok(
+ at("modelEffort") < at("requestReasoningEffort"),
+ `modelEffort must outrank requestReasoningEffort, got: ${chain}`
);
});
diff --git a/tests/unit/context-editing-relays.test.ts b/tests/unit/context-editing-relays.test.ts
index 3be153058f..6c5c9a8c33 100644
--- a/tests/unit/context-editing-relays.test.ts
+++ b/tests/unit/context-editing-relays.test.ts
@@ -75,7 +75,13 @@ test("F4.3: anthropic-compatible-cc-* relay → clear_tool_uses lands in the bod
model: "claude-opus-4-8",
body: { ...baseBody },
stream: false,
- credentials: { apiKey: "relay-key", baseUrl: "https://relay.example/v1" },
+ // #13452/#13798: the relay's baseUrl must live in providerSpecificData — a top-level
+ // credentials.baseUrl is never read on the chat path, and buildUrl() now refuses
+ // an unhydrated `*-compatible-*` node instead of falling back to the real API.
+ credentials: {
+ apiKey: "relay-key",
+ providerSpecificData: { baseUrl: "https://relay.example/v1" },
+ },
contextEditing: { enabled: true },
});
} finally {
@@ -95,7 +101,13 @@ test("F4.3: generic anthropic-compatible-* (non-cc) relay → NO context_managem
model: "claude-opus-4-8",
body: { ...baseBody },
stream: false,
- credentials: { apiKey: "relay-key", baseUrl: "https://relay.example/v1" },
+ // #13452/#13798: the relay's baseUrl must live in providerSpecificData — a top-level
+ // credentials.baseUrl is never read on the chat path, and buildUrl() now refuses
+ // an unhydrated `*-compatible-*` node instead of falling back to the real API.
+ credentials: {
+ apiKey: "relay-key",
+ providerSpecificData: { baseUrl: "https://relay.example/v1" },
+ },
contextEditing: { enabled: true },
});
} finally {
@@ -120,7 +132,13 @@ test("F4.2: upstream 400 rejecting context_management → strips it and retries
model: "claude-opus-4-8",
body: { ...baseBody },
stream: false,
- credentials: { apiKey: "relay-key", baseUrl: "https://relay.example/v1" },
+ // #13452/#13798: the relay's baseUrl must live in providerSpecificData — a top-level
+ // credentials.baseUrl is never read on the chat path, and buildUrl() now refuses
+ // an unhydrated `*-compatible-*` node instead of falling back to the real API.
+ credentials: {
+ apiKey: "relay-key",
+ providerSpecificData: { baseUrl: "https://relay.example/v1" },
+ },
contextEditing: { enabled: true },
});
} finally {
@@ -145,7 +163,13 @@ test("F4.2: an UNRELATED 400 does NOT strip context_management or retry", async
model: "claude-opus-4-8",
body: { ...baseBody },
stream: false,
- credentials: { apiKey: "relay-key", baseUrl: "https://relay.example/v1" },
+ // #13452/#13798: the relay's baseUrl must live in providerSpecificData — a top-level
+ // credentials.baseUrl is never read on the chat path, and buildUrl() now refuses
+ // an unhydrated `*-compatible-*` node instead of falling back to the real API.
+ credentials: {
+ apiKey: "relay-key",
+ providerSpecificData: { baseUrl: "https://relay.example/v1" },
+ },
contextEditing: { enabled: true },
});
} finally {
diff --git a/tests/unit/oauth-lan-loopback-guidance.test.ts b/tests/unit/oauth-lan-loopback-guidance.test.ts
index 8775c4ba7e..3f37c91de5 100644
--- a/tests/unit/oauth-lan-loopback-guidance.test.ts
+++ b/tests/unit/oauth-lan-loopback-guidance.test.ts
@@ -103,7 +103,10 @@ test("OAuthModal renders the structured panel instead of the generic error step"
assert.match(
modal,
- /else if \(isLocalhost\) \{[\s\S]{0,300}buildPkceLoopbackMismatchHint/,
+ // Match the isLocalhost arm regardless of extra conditions on it — #9944 added
+ // `&& !opts?.manualLoopback` so an operator can opt out. What must not regress is
+ // that this arm builds the structured hint, not that the condition stays bare.
+ /else if \(isLocalhost[^)]*\) \{[\s\S]{0,300}buildPkceLoopbackMismatchHint/,
"the isLocalhost arm of PKCE_CALLBACK_SERVER_PROVIDERS must build the structured hint"
);
assert.match(
diff --git a/tests/unit/probe-7293-strict-system-hoist.test.ts b/tests/unit/probe-7293-strict-system-hoist.test.ts
index bb66a38774..9dbbe35cad 100644
--- a/tests/unit/probe-7293-strict-system-hoist.test.ts
+++ b/tests/unit/probe-7293-strict-system-hoist.test.ts
@@ -4,9 +4,7 @@ import { translateRequest } from "../../open-sse/translator/index.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
function buildRepro(messageCount: number) {
- const messages: Array<{ role: string; content: string }> = [
- { role: "user", content: "hello" },
- ];
+ const messages: Array<{ role: string; content: string }> = [{ role: "user", content: "hello" }];
for (let i = 1; i < messageCount - 1; i++) {
messages.push({ role: i % 2 === 1 ? "assistant" : "user", content: `turn ${i}` });
}
@@ -174,7 +172,20 @@ test("#7293: Claude-source request keeps a single leading system message after c
.filter((i) => i >= 0);
assert.deepEqual(systemIndices, [0]);
- // Merge, never drop: both the top-level system and the offender survive.
assert.match(outMessages[0].content, /You are a coding assistant\./);
- assert.match(outMessages[0].content, /deferred tools list/);
+
+ // #12908 landed after #7293 and resolves the same constraint differently on this
+ // path: instead of folding a mid-array system into index 0, it demotes it to
+ // "user" in place, byte-identical. So the offender no longer merges — but it must
+ // still SURVIVE, which is the half of "merge, never drop" that actually protects
+ // the caller. The two strategies conflict, and the hoist additionally reorders the
+ // demoted turn ahead of the conversation; both are reported in #13948.
+ const offender = outMessages.find((m) => m.content === "deferred tools list");
+ assert.ok(offender, "the mid-array system instruction must not be dropped");
+ assert.equal(offender.role, "user", "it is demoted, not merged (#12908)");
+ assert.deepEqual(
+ outMessages.filter((m) => m.role === "user").map((m) => m.content),
+ ["deferred tools list", "hi", "go"],
+ "current ordering — the demoted turn is hoisted ahead of the conversation (#13948)"
+ );
});
diff --git a/tests/unit/provider-node-reserved-prefix.test.ts b/tests/unit/provider-node-reserved-prefix.test.ts
index ae1f0b15a0..df18b5e45e 100644
--- a/tests/unit/provider-node-reserved-prefix.test.ts
+++ b/tests/unit/provider-node-reserved-prefix.test.ts
@@ -182,7 +182,10 @@ test("shared set size includes live REGISTRY and retired Designer + Felo + Qwen
// #11786: SeekAi adds id "seekai" + alias "ska" (406 → 408).
// #13024 (2b9e7fb3e) GreenPT and #13025 (22473dee5) EURouter each add one REGISTRY member (id ==
// alias); #13277 (02128f334) registers Arcee AI, adding id "arcee-ai" + alias "arcee" (408 → 412).
- assert.equal(RESERVED_PREFIX_COUNT, 412);
+ // #13399 (cdcde97c7) registers Agnes AI (China): id "agnes-cn" + alias "agnescn" — the only
+ // two provider-level members added since; everything else in that range is model ids
+ // (412 -> 414). Same entry that moved the apikey/regional count to 241 in #13905.
+ assert.equal(RESERVED_PREFIX_COUNT, 414);
});
test("isReservedProviderPrefix rejects non-string input", () => {
diff --git a/tests/unit/reasoning-effort-clamp-and-retry.test.ts b/tests/unit/reasoning-effort-clamp-and-retry.test.ts
index b67451e6b4..9f830296b4 100644
--- a/tests/unit/reasoning-effort-clamp-and-retry.test.ts
+++ b/tests/unit/reasoning-effort-clamp-and-retry.test.ts
@@ -15,6 +15,14 @@ const OVH_422_BODY = JSON.stringify({
},
});
+// #13452/#13798: an `openai-compatible-*` connection must carry its own baseUrl, or
+// buildUrl() refuses to run rather than defaulting to the real OpenAI API. These cases
+// exercise reasoning_effort clamping, not URL resolution, so hydrate the connection the
+// way a configured one is.
+const OVH_CREDENTIALS = {
+ providerSpecificData: { baseUrl: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1" },
+};
+
// Passthrough executor: returns the body unchanged so we assert on exactly what
// base.ts sends upstream.
class SimpleExecutor extends BaseExecutor {
@@ -61,13 +69,18 @@ test("422 'unknown variant xhigh, expected one of ...' clamps reasoning_effort a
model: "qwen3-coder-30b-a3b-instruct",
body: { reasoning_effort: "xhigh" },
stream: false,
- credentials: {},
+ credentials: OVH_CREDENTIALS,
});
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "xhigh");
assert.equal(capturedBodies[1].reasoning_effort, "high");
assert.ok(
- (getLearnedReasoningEffort("openai-compatible-chat-eaff6869", "qwen3-coder-30b-a3b-instruct") as unknown as Set).has("high")
+ (
+ getLearnedReasoningEffort(
+ "openai-compatible-chat-eaff6869",
+ "qwen3-coder-30b-a3b-instruct"
+ ) as unknown as Set
+ ).has("high")
);
assert.equal(result.response.status, 200);
} finally {
@@ -99,7 +112,7 @@ test("a second request for the same provider+model sends the learned value on th
model: "qwen3-coder-30b-a3b-instruct",
body: { reasoning_effort: "xhigh" },
stream: false,
- credentials: {},
+ credentials: OVH_CREDENTIALS,
});
assert.equal(capturedBodies.length, 1);
assert.equal(capturedBodies[0].reasoning_effort, "high");
@@ -113,7 +126,10 @@ test("400 please use low, high, or max clamps and retries once (nearest-tier: me
const originalFetch = globalThis.fetch;
const capturedBodies: Record[] = [];
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" },
+ 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 = {}) => {
@@ -136,7 +152,7 @@ test("400 please use low, high, or max clamps and retries once (nearest-tier: me
model: "x-preview-f-free",
body: { reasoning_effort: "medium" },
stream: false,
- credentials: {},
+ credentials: OVH_CREDENTIALS,
});
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "medium");
@@ -144,7 +160,10 @@ test("400 please use low, high, or max clamps and retries once (nearest-tier: me
// high(4), the smallest accepted rank at or above it (was "low" under the
// old downgrade-only direction).
assert.equal(capturedBodies[1].reasoning_effort, "high");
- const learned = getLearnedReasoningEffort("openai-compatible-chat-eaff6869", "x-preview-f-free") as unknown as Set;
+ const learned = getLearnedReasoningEffort(
+ "openai-compatible-chat-eaff6869",
+ "x-preview-f-free"
+ ) as unknown as Set;
assert.ok(learned instanceof Set);
assert.ok(learned.has("low"));
assert.ok(learned.has("high"));
@@ -182,7 +201,7 @@ test("400 please use low, medium with ultra retries to medium", async () => {
model: "x-preview-f-free-2",
body: { reasoning_effort: "ultra" },
stream: false,
- credentials: {},
+ credentials: OVH_CREDENTIALS,
});
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "ultra");
@@ -225,7 +244,7 @@ test("sub-floor clamp now retries: learned {high,max} with low request clamps up
model: "x-preview-f-free-3",
body: { reasoning_effort: "low" },
stream: false,
- credentials: {},
+ credentials: OVH_CREDENTIALS,
});
assert.equal(capturedBodies.length, 2);
assert.equal(capturedBodies[0].reasoning_effort, "low");
diff --git a/tests/unit/settings-i18n-keys.test.ts b/tests/unit/settings-i18n-keys.test.ts
index 21b87fb7fe..5b9fc11e4e 100644
--- a/tests/unit/settings-i18n-keys.test.ts
+++ b/tests/unit/settings-i18n-keys.test.ts
@@ -197,6 +197,20 @@ function collectMissingEnglishDirectTranslationKeys() {
const key = match[3].replace(/\\(['"\\])/g, "$1");
const fullKey = binding.namespace ? `${binding.namespace}.${key}` : key;
if (typeof lookupMessage(en, fullKey) === "string") continue;
+ // A dynamically-built key — t("effort." + rule.effortMode) — reaches us as the
+ // literal prefix with its trailing dot, so it never resolves to a string. Accept
+ // it when the prefix resolves to a namespace that actually holds messages; a
+ // prefix that does not exist at all still fails, which is what this guards.
+ if (fullKey.endsWith(".")) {
+ const namespace = lookupMessage(en, fullKey.slice(0, -1));
+ const holdsMessages =
+ namespace !== null &&
+ typeof namespace === "object" &&
+ Object.values(namespace as Record).some(
+ (value) => typeof value === "string"
+ );
+ if (holdsMessages) continue;
+ }
const relative = path.relative(process.cwd(), file);
const line = raw.slice(0, match.index).split(/\r?\n/).length;
diff --git a/tests/unit/sse-auth.test.ts b/tests/unit/sse-auth.test.ts
index 53cd373228..40708fe4ee 100644
--- a/tests/unit/sse-auth.test.ts
+++ b/tests/unit/sse-auth.test.ts
@@ -769,7 +769,11 @@ test("getProviderCredentials refuses a forced pin outside allowedConnections ins
// policy-allowed pool keeps its constraint — resolution yields no credential instead
// of silently continuing on another connection. The policy-blocked connection must
// never be selected, and the allowed one must not be picked behind the caller's back.
- assert.equal(selected, null);
+ // #13879 returns the key-policy diagnostic here instead of a bare null — the shape
+ // the terminal-state path has used since #12441 — so chat answers 403 rather than
+ // the generic "No active credentials". deepEqual pins it exactly, which is what
+ // #12080 needs: no apiKey/accessToken/connectionId, and neither connection leaks.
+ assert.deepEqual(selected, { blockedByKeyPolicy: true, blockedCount: 1 });
});
test("getProviderCredentials retains rate-limited accounts when allowSuppressedConnections is enabled", async () => {
diff --git a/tests/unit/stream-early-eof-affinity-8928.test.ts b/tests/unit/stream-early-eof-affinity-8928.test.ts
index 2af93bd152..8938887d60 100644
--- a/tests/unit/stream-early-eof-affinity-8928.test.ts
+++ b/tests/unit/stream-early-eof-affinity-8928.test.ts
@@ -8,20 +8,17 @@ const chatSource = fs.readFileSync(
);
function getNonAntigravityStreamFailureBranch(): string {
- const startMarker = [
- " if (",
- ' (result.errorType === "stream_timeout" || result.errorType === "stream_early_eof") &&',
- " !isAntigravityStreamReadinessFailure",
- " ) {",
- ].join("\n");
+ // Locate the branch by what it TESTS, not by how it is formatted: #12906 added
+ // `|| result.errorCode === "empty_response"` to the same condition and Prettier
+ // rewrapped it, which a literal marker could not survive. What must not regress
+ // is that a non-Antigravity stream failure still reaches the eviction below.
+ const branchRe =
+ /if \(\s*\([\s\S]{0,400}?stream_early_eof[\s\S]{0,240}?\)\s*&&\s*!isAntigravityStreamReadinessFailure\s*\)\s*\{/;
+ const match = branchRe.exec(chatSource);
+ assert.ok(match, "non-Antigravity stream-failure branch must exist");
- const start = chatSource.indexOf(startMarker);
- assert.notEqual(start, -1, "non-Antigravity stream-failure branch must exist");
-
- const end = chatSource.indexOf(
- "\n if (isAntigravityStreamReadinessFailure)",
- start
- );
+ const start = match.index;
+ const end = chatSource.indexOf("\n if (isAntigravityStreamReadinessFailure)", start);
assert.notEqual(end, -1, "stream-failure branch end marker must exist");
return chatSource.slice(start, end);
@@ -31,12 +28,8 @@ test("terminal STREAM_EARLY_EOF evicts affinity after the bounded retry (#8928)"
const branch = getNonAntigravityStreamFailureBranch();
const retryContinue = branch.indexOf("continue;");
- const eviction = branch.indexOf(
- "evictSessionAccountAffinityForConnection("
- );
- const terminalReturn = branch.indexOf(
- "return withSelectedConnectionHeader("
- );
+ const eviction = branch.indexOf("evictSessionAccountAffinityForConnection(");
+ const terminalReturn = branch.indexOf("return withSelectedConnectionHeader(");
assert.ok(retryContinue >= 0, "the existing bounded retry must remain");
assert.ok(eviction > retryContinue, "eviction must happen only after retry is exhausted");
diff --git a/tests/unit/zcode-executor.test.ts b/tests/unit/zcode-executor.test.ts
index e4a3d4092f..cab8f4b4f6 100644
--- a/tests/unit/zcode-executor.test.ts
+++ b/tests/unit/zcode-executor.test.ts
@@ -1,6 +1,6 @@
import test from "node:test";
import assert from "node:assert/strict";
-import { mkdtempSync, rmSync } from "node:fs";
+import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { cleanupTempDataDir } from "../_setup/tempDataDir.ts";