Repository navigation
Release v3.8.29 - #4126
Merged
Merged
Release v3.8.29#4126
Conversation
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Contributor
CI Coverage Report
Coverage artifact was not available for this run. |
Integrated into release/v3.8.29
Integrated into release/v3.8.29
Integrated into release/v3.8.29
Integrated into release/v3.8.29 with review fixes (narrowed text-scan redaction + documented OMNIROUTE_VSCODE_SANITIZE_CONTEXT + regression tests). Co-authored-by: diegosouzapw
) Two integration tests were red on `main` after the v3.8.28 release merged. Integration tests do not gate PR->release, so these only surfaced on push->main once the cycle's changes landed: - v1-contracts-behavior count_tokens expected 3 (the old ceil(chars/4) heuristic). #4087 switched the estimator to real tiktoken (countTextTokens): "abcd" => 1, "12345678" => 3, total 4. Assertion updated to the verified tiktoken total. - combo-routing-e2e round-robin tests assumed one-request-per-target rotation, but stickyRoundRobinLimit defaults to 3 and sticky batching has applied to combos since v3.8.26 (#3846). Both tests now drive enough requests to observe the batched rotation: round-robin cycles openai x3 -> claude x3 -> gemini x3; the strategy-update test proves round-robin took over by reaching claude on the 4th post-update request (a target priority would never select). Test-only; no production behavior change. Verified locally: both files green.
…-toolchain regression (#4134) Quality-Gate v2 Fase 9 maintenance (two findings from the Onda 0-1 follow-up): - check-openapi-breaking.mjs: DEFAULT_BASE_REF was hard-coded "origin/release/ v3.8.27" and drifted into the v3.8.29 cycle — LOCAL runs of the oasdiff breaking-change gate diffed the PR spec against the wrong release (CI passes BASE_REF=${{ github.base_ref }} and was unaffected). Derive it from package.json via releaseBranchForVersion() so it tracks every release bump automatically; literal fallback bumped to v3.8.29. +3 TDD cases. - docs/ops/MUTATION_GATE_STRYKER_REGRESSION.md: NEW toolchain regression — `npx stryker run` now fails at config validation under Stryker 9.6.1 + ajv 8.18.0 with a false "concurrency must match pattern" (reproduced with the UNMODIFIED stryker.conf.json, and with concurrency removed). The project config is valid — a standalone ajv 8.18.0 (Stryker's own dep) accepts it — so it's an OptionsValidator regression, not our config. This is NOT a re-opening of the Task 12 spike (perTest/killedBy was settled GO in v3.8.27; the nightly ran as recently as v3.8.28, timing out on budget). The nightly now can't even start; clearing this (a dependency pin needing a non-shared reinstall) is the prerequisite to producing scores again. Doc records evidence + fix direction.
…ns (#4132) Two unit tests flaked under CI parallelism (reproduced locally under CPU contention, both RED → GREEN after the fix): - db-backup-extended "creates manual backups": waited only for the backup FILE to exist, but backupDbFile() fires db.backup() fire-and-forget and better-sqlite3 creates the destination at copy START → listDbBackups() read a partial backup as connectionCount 0 != 12. Now waits for the real completion condition (backup contains the seeded rows); 30s ceiling only bounds the failure case, green path returns sub-second. - chatcore-translation-paths "streaming without buffering": raced the invocation against a fixed 1000ms budget; a starved event loop made the legitimate (non-buffering) early return exceed 1s -> false "blocked". Widened the ceiling to 10s (matches the file's existing waitFor pattern); the buffering-regression detection is unchanged. Repro: concurrent copies under CPU burners -> both RED; after fix both GREEN at realistic (8+8 copies) and extreme (12+12 copies + 14 burners) contention. Test-only change; no production code touched. (The pre-commit prettier --write also normalized one pre-existing non-conforming region in chatcore-translation-paths.test.ts.)
…-concurrency); record Task 12 GO (#4138) The MUTATION_GATE_STRYKER_REGRESSION.md note shipped in #4134 was WRONG: there is no Stryker 9.6.1 OptionsValidator regression. The false "concurrency must match pattern" failure was operator error — `stryker -c <file>` sets --concurrency, not the config file (which is the positional arg), so concurrency became the string "stryker.conf.json". `npx stryker run` (auto-discovery) validates and runs fine: it instruments 15488 mutants across the 8 modules. Renamed -> MUTATION_GATE_SPIKE_VERDICT.md and rewritten with: - the corrected record + the `-c` CLI gotcha; - the real Task 12 per-test spike verdict (GO, Plan A): a scoped run on open-sse/utils/error.ts killed 86/422 mutants, ALL 86 with a populated `killedBy` resolving to a test file -> per-test attribution works (per-file granularity, as expected for the tap-runner). Onda 2 can use killedBy directly. - the standing Onda-2 blocker is budget (god-files), not tooling.
…-mutation scope) (#4135) * chore(quality): re-baseline v3.8.28 cycle drift (eslint/openapi/i18n) The post-release push->main Quality Ratchet (run 27725117464) failed on 3 metrics that drifted during the v3.8.28 cycle from legitimate feature merges: eslintWarnings 3769->3779, openapiCoverage.pct 38.3->37.6, i18nUiCoverage.pct 80.1->79.1. Reproduced locally on release/v3.8.29 (9f14c12) — identical to CI. None is hand-cleanable without gaming or unavailable infra: - eslint +10: no-explicit-any is warn-level and allowed in tests (the drift is test `any`), plus 4 react-hooks/exhaustive-deps in RequestLoggerV2.tsx (a refresh-sensitive component — touching hook deps without UI tests is risky). - openapi -0.7: the drop is from new INTERNAL routes (/api/tools/agent-bridge/*, LOCAL_ONLY) — documenting them in the public spec would game the metric. - i18n -1.0: 37/41 locales need ~3000 translations via `npm run i18n:run`, which requires OMNIROUTE_TRANSLATION_API_KEY (not available locally). Re-baselined to the real measured values per the documented v3.8.26 precedent (_eslint_rebaseline_2026_06_16_v3826_forward_merge). Tighten at cycle end: eslint/openapi via --require-tighten; i18n via i18n:run with creds. * ci(mutation): scope nightly to 6 modules to fit the 180min budget The full 8-module Stryker run timed out at the 180min nightly cap (run 27705123780: 16:47:33 -> 19:47:48 = exactly 180min; the prior 120min scheduled run also timed out). #4078's DATA_DIR isolation made concurrency=4 safe but not sufficient: the tap-runner re-spawns a node process per test file per mutant, so spawn cost dominates and the two god-files chatCore.ts (5874 LOC) + combo.ts (5282 LOC) — ~2/3 of the ~15k mutants — do not fit. Removed both god-files from stryker.conf.json `mutate`. The 6 smaller modules fit the budget and produce real mutation scores now; the god-files regain coverage after the Onda 3 split into smaller units. Updated the nightly job name/comments (8->6 modules). Removing entries from `mutate` does not affect the dry-run baseline (it runs tap.testFiles, unchanged), so the all-green baseline is preserved.
…, opt-in) (#4131) Integrated into release/v3.8.29
Integrated into release/v3.8.29
Integrated into release/v3.8.29
…4137) Integrated into release/v3.8.29 (moved component test into tests/unit/ui/; check:test-discovery green). Co-authored-by: diegosouzapw
Integrated into release/v3.8.29
… port, Fase 6) (#4136) Free DuckDuckGo web search as last-resort provider (FCC port, Fase 6). Integrado em release/v3.8.29: conflito de file-size-baseline.json resolvido (união dos rebaselines duckduckgo_free + stream_recovery) e teste de contagem de providers atualizado 12->13.
Comment on lines
+39
to
+41
| return text | ||
| .replace(/ /gi, " ") | ||
| .replace(/&/gi, "&") |
| } | ||
|
|
||
| function stripTags(html: string): string { | ||
| return decodeEntities(html.replace(/<[^>]+>/g, "")).replace(/\s+/g, " ").trim(); |
| }); | ||
|
|
||
| assert.equal(result.success, true); | ||
| assert.ok(capturedUrl.includes("lite.duckduckgo.com"), "must hit the DDG lite endpoint"); |
| test("handleSearch fails over to duckduckgo-free when the primary provider errors", async () => { | ||
| const originalFetch = globalThis.fetch; | ||
| globalThis.fetch = (async (url: string | URL) => { | ||
| if (String(url).includes("lite.duckduckgo.com")) { |
…form controls (design phases 1-4) (#4122) Unified visual identity (design phases 1-4): grid, primitives, tables, form controls. Integrado em release/v3.8.29: clsx + tailwind-merge adicionados à dependency-allowlist (já declarados no package.json, consumidos por src/shared/utils/cn.ts) para o check:deps anti-slopsquatting passar.
Fase 3 spike — TPROXY command builder (pure, execFile-safe {bin,args[]} per Rule #13) + ProxyBridge capture-source decision. Integrado em release/v3.8.29.
…#4140) Credential-redaction safety net (FCC port, Fase 8.3): defense-in-depth pino logMethod hook, ReDoS-bounded patterns, behavior-preserving. Integrado em release/v3.8.29.
…ent (C6) (#4141) Grid wallpaper opacity tuned up so it's actually visible on the dense dashboard: light 0.045->0.07, dark 0.035->0.06 (the site's 0.045/0.035 read as invisible behind the cards/chrome that cover most of the viewport). Focus-ring reconcile (design.md C6): the form controls (Input, Select, Textarea, Toggle, Checkbox) now focus on the accent (violet) ring to match the global --focus-ring, instead of the red ring-primary that collided with the red error state. Error rings stay red. Guard test updated (grid opacity + new C6 assertion). Build verified: --grid-line compiles to #12 / #ffffff0f and focus:ring-accent/30 + focus:border-accent/50 utilities are generated.
…nerate per-provider table (#4142) Follow-ups from the 2026-06-17 free-tier refresh (#4089) that did NOT land in the original merge — orphan commit ccc13c2 was pushed after the PR had already merged, so these three follow-ups never shipped. Re-applied onto release/v3.8.29. - providers.ts: hasFree flipped to false for confirmed-dead free tiers (phind x2, kluster, glhf, chutes) so onboarding stops advertising a free tier that no longer exists. Deliberately NOT flipped: gitlawb (dedicated test guards hasFree:true), aimlapi/theoldllm (medium-confidence research) — flipping risks a qwen-web-style false removal. - New test tests/unit/discontinued-providers-2026.test.ts guards both the flips and the intentional keeps. - Headline rounded ~1.5B -> ~1.6B in README (hero/badge/intro/bullet), FREE_TIERS honest-headline sentence, and FREE-TIERS-GUIDE total. The TL;DR table + live card + SVG keep the precise computed ~1.54B. - FREE_TIERS per-provider table regenerated from the per-model catalog (pool-deduped, 2026-06-17): the old inflated rate-limit rows (sparkdesk 2.59B / tencent 2.07B / siliconflow 1.73B) are gone — uncapped providers now show 'uncapped*'.
…r) (#4143) Owner found the 46px graph-paper cells a touch large for the dashboard layout; tightening --grid-size to 32px (a ~30% reduction). Mirrors the matching change on the marketing site so the two stay identical. Guard test + design.md updated. Build verified: --grid-size compiles to 32px.
No-thinking gateway model IDs (FCC port, Fase 8.1): synthetic claude-3-omniroute-no-thinking/<provider>/<model> catalog id that resolves to the real model with reasoning suppressed. Integrado em release/v3.8.29.
… Fase 8.2) (#4146) Per-provider sliding-window rate-limit fallback (FCC port, Fase 8.2): opt-in (empty map = no-op), burst-free sliding window as a floor for header-less providers; Bottleneck still applies. Integrado em release/v3.8.29 (conflito de file-size-baseline resolvido por união com #4145).
…se 9 T9) (#4149) Harden fabricated-docs checker (precision: env helpers/code-identifiers/api-prefixes/env-contract/tutorial-placeholders) + enforce --strict (QG v2 Fase 9 T9). Inclui testes anti-over-suppression + correção de refs stale de docs. Integrado em release/v3.8.29.
…(prebuilds 4e) (#4236) Integrated into release/v3.8.29
Integrated into release/v3.8.29
…(T1) + mutationScore ratchet (T3) (#4234) Integrated into release/v3.8.29
…s sub-route (#4237) Integrated into release/v3.8.29
…ll toggle (#4238) Integrated into release/v3.8.29
…Docstrings (#4242) Integrated into release/v3.8.29
#4244) Integrated into release/v3.8.29
Integrated into release/v3.8.29
…strip + verbosity passthrough (#4245) Integrated into release/v3.8.29 (baseline reconciled)
…endpoints + motor de custo não-token (#4247) Integrated into release/v3.8.29 (baseline reconciled: chatCore 5111, combo 2605)
…ias (#4253) Integrated into release/v3.8.29 — remove vestigial SLM seam (createSLMStub/SLMInterface) + dead estimateTokensForStats alias. Validated: ultra.test.ts 39/39 green, zero code callers of the removed symbols.
… (#4250) Integrated into release/v3.8.29 — Cursor Cloud Agent (cursor-cloud) via official api.cursor.com REST API key, 4th cloud agent mirroring Devin/Jules. Pre-merge work done on the PR branch (fix-in-place): - Wired cursor-cloud into the dashboard UI (CLOUD_AGENTS dropdown), health PROVIDER_NAMES, and lobeProviderIcons — it was API-usable but not selectable in the UI and rendered with the Jules fallback. - Fixed cloud-agent-health-api.test.ts which asserted exactly 3 agents (the PR added a 4th) -> updated count/order + added cursor-cloud coverage. - file-size baseline: page.tsx 913->922. FOLLOW-UP (Rule #18): the REST endpoint paths/field names follow Cursor's documented Cloud Agents v0 API but still need a live validation run against a real Cursor API key. The integration is additive/opt-in and baseUrl is overridable per-credential, so any path/version correction needs no code change. Thanks @MRDGH2821 for the issue (#4227).
…) + extend to c/d/g/h (#4258) Restore cold-seed timeout headroom in nightly-mutation (per-batch timeout: a/b/c/d=350, g/h=240, rest=180). A cold Stryker run must complete once to seed stryker-incremental.json; a job cancelled at the cap never seeds. Integrated into release/v3.8.29.
Keep optional deps on update: add --include=optional to the CLI update, auto-updater, and source-update scripts so optionalDependencies (better-sqlite3, keytar, tls-client, llmlingua SLM stack) survive an omit=optional config. Test-guarded (RED→GREEN). Integrated into release/v3.8.29.
…ease Full quality gate (test:unit + test:vitest + check:docs-all) caught drift from cycle PRs that the PR→release fast-gates do not run. Each fix aligns to the already-merged code (no behavior change): - test(cloud-agent): expect 4 registered agents incl. cursor-cloud (#4250) - test(db-rules): add accessTokens to INTENTIONALLY_INTERNAL audited set (#4256) - test(glm): GLM/GLM-CN coding timeout 900000 -> 3000000 (50min) (#4255) - fix(i18n): add connect/tokens/configure CLI locale sections to pt-BR.json (#4256) - docs(env): document OMNIROUTE_CONTEXT in .env.example + ENVIRONMENT.md (#4256) - fix(ci): fabricated-docs checker now indexes arg-bearing .command("x <a>") forms (false-negative flagged a real `omniroute connect`); +2 regression tests
… test-masking allowlist) Release-PR (→main) quality gates fired on cumulative cycle drift the PR→release fast-gates don't run: - eslintWarnings ratchet 3779 -> 3816: +37 `any` (warn-level) warnings from 115 cycle commits in open-sse/ and tests/. Release reconciliation adds 0 warnings (scripts/check/*.mjs are eslint-ignored; new fabricated-docs test uses no any). Same precedent as the v3.8.28 cycle-close rebaseline. Documented in baseline. - test-masking allowlist += 5 verified-legitimate net-assert reductions from merged refactors: #4129 (combo-routing-e2e alignment), #4226 (ccr-marker/session-dedup helper removal), #4253 (ultra SLM-seam removal), #4132 (db-backup de-flake). Each reduction removed/restructured asserts for removed code — not weakening.
|
… cycle improvements Require-tighten (blocking) flagged 2 metrics that IMPROVED this cycle (CI-measured on PR #4126): coverage.auth.lines 69->90 (CI 92.6; ~2pt anti-flake floor) and openapiCoverage.pct 37.6->38.4. Legitimate improvements locked into the baseline.
HouMinXi
pushed a commit
to HouMinXi/OmniRoute
that referenced
this pull request
Aug 2, 2026
OmniRoute v3.8.29 — 115 commits since v3.8.28. Full CHANGELOG + 41 i18n mirrors. All content quality gates green (build, unit 8/8, vitest 188/188, PR test policy, quality gates extended, docs sync, quality ratchet). Remaining red CI checks are pre-existing release flakes (coverage-shard/integration/node-compat teardown), a new transitive undici advisory in electron devDeps, and a workflow-level CodeQL fail (0 open alerts). VPS-validated by the operator.
Poid-ZA
pushed a commit
to Poid-ZA/OmniRoute
that referenced
this pull request
Aug 5, 2026
OmniRoute v3.8.29 — 115 commits since v3.8.28. Full CHANGELOG + 41 i18n mirrors. All content quality gates green (build, unit 8/8, vitest 188/188, PR test policy, quality gates extended, docs sync, quality ratchet). Remaining red CI checks are pre-existing release flakes (coverage-shard/integration/node-compat teardown), a new transitive undici advisory in electron devDeps, and a workflow-level CodeQL fail (0 open alerts). VPS-validated by the operator.
tkgo11
pushed a commit
to tkgo11/OmniRoute
that referenced
this pull request
Sep 23, 2026
… cycle improvements Require-tighten (blocking) flagged 2 metrics that IMPROVED this cycle (CI-measured on PR cheaper-inference#4126): coverage.auth.lines 69->90 (CI 92.6; ~2pt anti-flake floor) and openapiCoverage.pct 37.6->38.4. Legitimate improvements locked into the baseline.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
OmniRoute v3.8.29 — 115 commits since v3.8.28. Full CHANGELOG + 41 i18n mirrors. All content quality gates green (build, unit 8/8, vitest 188/188, PR test policy, quality gates extended, docs sync, quality ratchet). Remaining red CI checks are pre-existing release flakes (coverage-shard/integration/node-compat teardown), a new transitive undici advisory in electron devDeps, and a workflow-level CodeQL fail (0 open alerts). VPS-validated by the operator.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




[3.8.29] — 2026-06-19
✨ New Features
cursor-cloudcloud agent that drives Cursor's Background / Cloud Agents through the official REST API (api.cursor.com) authenticated with a user or service-account API key — the safer, first-party alternative to re-using the Cursor IDE's OAuth session (the existingcursorprovider, which carries a ban-risk warning). Implemented as a plain REST adapter mirroring the Devin/Jules agents (createTask/getStatus/sendMessage/listSources), so it does not pull in the@cursor/sdkpackage and its per-platform native binaries (Cursor's SDK is itself a thin wrapper over this REST API). Cursor's UPPERCASE status enums (CREATING/RUNNING/FINISHED/ERROR) are mapped explicitly to the sharedCloudAgentStatus, andbaseUrlis overridable per-credential. Credentials are stored encrypted via the existingcloud_agent_credentialstable; no schema change. (#4227 — thanks @MRDGH2821)auto/<category>:<tier>combos — auto-routing now understands suffixed combos that separate the category (what kind of route) from the tier (how to optimize):auto/coding:fast,auto/coding:cheap(alias:floor),auto/coding:free,auto/coding:pro,auto/coding:reliable, plus the new category rootsauto/reasoning,auto/vision,auto/multimodal. The tier picks the scoring weights —:fast→ ship-fast,:cheap/:floor→ cost-saver,:reliable→ a new reliability-first pack (circuit-breaker health + latency stability) — while:free/:profilter the candidate pool by model tier (classifyTier: free-tier vs. premium models). The category filters the pool by capability (vision/multimodal→ vision-capable models,reasoning→ reasoning/thinking models). Any validauto/<category>:<tier>resolves on demand; a curated set is advertised in/v1/modelsand the dashboard. Filtering is fail-open — if a constraint matches no connected models the full pool is used so routing never breaks. All composition lives in the newopen-sse/services/autoCombo/suffixComposition.ts; the core combo scorer (combo.ts) is untouched. Second slice of [Feature] Enhancedauto/*combos #4235 (premium account-tier weighting is a later follow-up). (#4235 — thanks @MRDGH2821)auto/cheap,auto/offline,auto/smartcombos (catalog ↔ README sync) — the README listsauto/cheap(cheapest-per-token first),auto/offline(most quota/rate-limit headroom first) andauto/smart(quality-first + 10% exploration), and they already resolved at request time viaparseAutoPrefix→createVirtualAutoCombo. But they were missing fromAUTO_TEMPLATE_VARIANTS, so/v1/modelsand the dashboard combos list (which iterate that catalog) never showed them — the catalog drifted from the docs (visible in the issue's screenshots). Added the three entries so they're advertised everywhere alongside the other built-inauto/*combos. First slice of [Feature] Enhancedauto/*combos #4235 (OpenRouter-styleauto/<category>:<tier>suffixes + new categories follow). (#4235 — thanks @MRDGH2821)X-OmniRoute-*headers on every endpoint + a non-token cost engine — every endpoint now emits theX-OmniRoute-*cost/usage headers, backed by a cost engine that also prices non-token (media/request-based) usage. (#4247)kimi-k2.7-code+-highspeed) — the new Moonshot thinking-only coding models are registered (fixed sampling;temperature/top_pmarked unsupported). (#4183)kimi-k2.7-codeto the kmca catalog + qwen-web models discovery — surfaces the new Kimi coding model in the kmca catalog and wires qwen-web into model discovery. (#4185)zaiprovider catalog with GLM-5.2 / GLM-4.7 — adds the real GLM-5.2, GLM-4.7 and GLM-4.7-flash model ids to the Anthropic-directzaiprovider. (#4201)update(--include=optional) — the in-place update path now passes--include=optionalso native/optional packages aren't dropped on update. (#4260)serviceKindsfrom the registries (surface MiniMax + the media catalog) —/media-providers/[kind]now derives its service kinds from the registries instead of a hand-maintained list, surfacing ~48 previously-invisible media providers (incl. MiniMax TTS/video/music). (#4212)IP_TRANSPARENTaddon with a conditional loader, the foundation for TPROXY capture. (#4148)setSocketMarkto the TPROXY addon (anti-loop primitive) — exposessetSocketMarkso OmniRoute's own egress can be marked and skipped (anti-loop). (#4160)connectMarked(Epic A) — the capture-mode listener plus a marked-connect primitive. (#4169)compression.step(F3.3) — streams per-engine compression progress through acompression.stepevent. (#4217)mcpAccessibilityconfig reachable via a settings sub-route — exposes themcpAccessibilityconfig under a dedicated settings sub-route. (#4237)preserveDocstrings— RTK comment-stripping is now configurable and honors apreserveDocstringsflag. (#4242)deduplicateflag — RTK filters now respect a per-filterdeduplicateflag. (#4231)enabledflag in the stacked loop — the stacked compression loop now skips engines disabled in the registry. (#4244)enableGrouping) — persists the RTK grouping configuration, unlocking the R5enableGroupingrule. (#4207)modelPath/slmFallbackToAggressiveto the LLMLingua SLM tier — connects the ultra tier's small-language-model knobs to the LLMLingua SLM path. (#4257)mutationScoreratchet (T3) — new mutation-testing tooling: a survivor-radiography classifier and amutationScoreratchet. (#4234)🐛 Fixed
qwen-webcookie provider had no entry inPROVIDER_MODELS_CONFIG, so its model-discovery page returned an empty/stale local catalog (the OAuth fallback at the top of the route only fires forprovider === "qwen", leavingqwen-webto fall through to the no-config branch). Added aqwen-webentry that fetches the publichttps://chat.qwen.ai/api/v2/modelsendpoint (no auth header) and parses the{ data: { data: [{ id, name, owned_by }] } }shape (with a flatter{ data: [] }fallback). This is Problem deps: bump eslint from 9.39.2 to 10.0.0 in the development group #3 of fix(qwen-web): validator false-positive + empty stream with valid token #3931 (diagnosed by @thezukiru); Problem [WIP] Add FastAPI OpenAI-compatible LLM gateway with routing #1 — validator bare-token false-positive — shipped earlier in fix(qwen-web): cookie validation false-positive - check response body for user object #3958, and Problem build(deps): bump actions/github-script from 7 to 8 #2 — empty stream from Qwen WAF bot-detection on the streaming endpoint — remains a separate upstream/stealth concern. (#3931 — thanks @thezukiru)API unavailable — using local catalogand was missing the free models ZenMux advertises (z-ai/glm-5.2-free,moonshotai/kimi-k2.7-code-free). Root cause:zenmuxcarries a correctmodelsUrlin the registry, but — likellm7/byteplusbefore [BUG] LLM7 GET /models does not work #3976 — it was not classified by any live-fetch branch of the model-import route (notopenai-compatible-*, not self-hosted, not inNAMED_OPENAI_STYLE_PROVIDERS), so the route never probed the upstream/modelsand fell through to the registry's hardcodedmodels[]. AddedzenmuxtoNAMED_OPENAI_STYLE_PROVIDERS, so the route probeshttps://zenmux.ai/api/v1/models(the/chat/completions-stripped<baseUrl>/modelscandidate) and serves the live list, falling back to the local catalog only when the upstream fetch fails — import never breaks. (#4202 — thanks @mikmaneggahommie)vercel-ai-gatewaycarries a realbaseUrl(https://ai-gateway.vercel.sh/v1/chat/completions, formatopenai) in the registry, but was not classified by any live-fetch branch of the model-import route (notopenai-compatible-*, not self-hosted, not inNAMED_OPENAI_STYLE_PROVIDERS), so the route never probed the upstream/modelsand fell through to the registry's tiny 5-entry hardcodedmodels[]. Addedvercel-ai-gatewaytoNAMED_OPENAI_STYLE_PROVIDERS, so the route probeshttps://ai-gateway.vercel.sh/v1/models(the/chat/completions-stripped<baseUrl>/modelscandidate) and serves the live list, falling back to the local catalog only when the upstream fetch fails — import never breaks. (#4249 — thanks @FerLuisxd)resilienceSettings.requestQueue.maxWaitMs) were dropped by Bottleneck with its rawThis job timed out after <maxWaitMs> ms.message. That string is indistinguishable from an upstream gateway timeout, so the 502 body and call-loglast_errorlooked like a provider outage across unrelated providers (TI:0|TO:0) — an operator spent ~3h misdiagnosing local queue saturation as upstream failures.withRateLimitnow rewrites that specific Bottleneck error into a clear, OmniRoute-owned message that names the knob (requestQueue.maxWaitMs, tunable in Settings → Resilience), explicitly disclaims an upstream timeout, preserves the original ascause, and tagscode: "RATE_LIMIT_QUEUE_TIMEOUT". Behavior is unchanged — the job is still dropped so combo falls back to the next target. (#4165 — thanks @KooshaPari)auto/*combos in/v1/models— OmniRoute ships a zero-setupauto/*catalog (auto/best-coding,auto/pro-reasoning, …, 16 variants) that the dashboard advertises and that resolve on demand, but the/v1/modelslisting only emitted persisted DB combos + provider models. Clients that build their model picker from/v1/models(e.g. Hermes Agent) never saw anyauto/*option. The catalog now emits everyAUTO_TEMPLATE_VARIANTSid (asowned_by: "combo") at the top of the list, deduped against persisted combos. (Showing eachauto/*'s dynamically-selected members is a separate enhancement.) (#4164 — thanks @MRDGH2821)Error: No such tool available: <PascalCaseName>: tool schemas arrived fine but the streamedtool_use.namereached Claude Code in its cloaked form (e.g.McpN8nMcpSearchWorkflowsinstead of the registeredmcp__n8n-mcp__search_workflows). The native-Claude tool-name cloak stashes its per-request alias→original map as a non-enumerable_toolNameMapon the request body; the request-inspector capture added in 3.8.27 rebuilds the captured body from its serialized form (JSON.parse(JSON.stringify(...))), which drops non-enumerable properties, sofinalBody._toolNameMapwas empty and the response-side un-cloak silently fell back to the static built-in map — never restoring dynamic MCP / snake_case names. Built-in tools (Bash/Read/…) were unaffected (static map); cross-format paths were unaffected (they attach the map enumerably). The provider-request capture now re-attaches the per-request map (kept non-enumerable, so it still never re-serializes upstream) when the captured copy lost it, restoring MCP tool dispatch. (#4091 — thanks @pedrotecinf, @NakHalal)visibilitychange→ hidden and then keeps reporting"hidden"(or recovers without firing the event again) left the cached visibility flag stuckfalse, so the interval ticked but never polled (only the manual Refresh button worked). The poll tick now also re-checks the livedocument.visibilityState, and a windowfocuslistener re-arms polling (a focused window is a reliable signal the page is actively viewed). A genuinely backgrounded browser tab still pauses (it reports"hidden"and never receives focus), preserving the perf(logs): fix browser freeze and network saturation on /dashboard/logs #3109 network-saturation optimization. (#4133 — thanks @tjengbudi)/v1/modelslist was too broad, flagging text models (gemma, barekimilikekimi-k2) as vision. All three (modelCapabilitiesrouting fallback,/v1/modelslisting, lite image-strip gate) now delegate to a single conservative sourcesrc/shared/constants/visionModels.ts, which also restoresglm-4v/gemini-3coverage and keeps the is mini max 3 blind on opencode provider #3328 MiniMax M3 carve-out. (#4072 — thanks @diego-anselmo){"error":{"code":503,"message":"…high demand…","status":"UNAVAILABLE"}}) instead of acandidatespayload, OmniRoute silently dropped it: the gemini→openai translator's no-candidate branch only handledpromptFeedback(content-filter) and returnednullfor anything else, so the stream simply ended and the client got HTTP 200 with a truncated body andfinish_reason: "stop"— masking the failure and skipping combo fallback.geminiToOpenAIResponsenow detects anerrorobject (optionally wrapped inresponse), records it asstate.upstreamError(preserving the real status — 503/UNAVAILABLE, or 429 forRESOURCE_EXHAUSTED), and letsstream.tserror the stream out through the existingonFailure/buildErrorBody/controller.errorpath — the same mechanism the openai-responses translator already uses. (#4177 — thanks @hartmark)-latestaliases — root cause behind the fix(sse): route image requests only to confirmed-vision combo targets #4071 heuristic:getResolvedModelCapabilities("mistral/pixtral-12b-latest").supportsVisionresolvednull(vision came only from the fix(sse): route image requests only to confirmed-vision combo targets #4071 model-id heuristic, withattachmentstillnull) even though models.dev exposes the model as multimodal. Confirmed against the live models.dev API: it catalogs Pixtral 12B under the short idpixtral-12b(withattachment: true,modalities.input: ["text","image"]), while requests use the Mistral API aliaspixtral-12b-latest. The synced lookup tried the exact / raw / static-spec-canonical ids — all of which miss the short form — so it fell through to the heuristic.getSyncedCapabilityForResolvednow adds a last-resort fallback that retries with a trailing-lateststripped, so synced metadata (attachment/ image modalities) wins for these aliases; models whose-latestid is stored verbatim (e.g.pixtral-large-latest) keep resolving directly. Note: the models.dev sync is currently manual-only (Settings → models.dev) with no scheduled refresh, so a fresh instance still relies on the fix(sse): route image requests only to confirmed-vision combo targets #4071 heuristic until that sync runs — a periodic-refresh cadence is left as a separate follow-up. (#4073 — thanks @diego-anselmo)thinking:{type}shape — MiMo (api.xiaomimimo.com) controls chain-of-thought only via top-levelthinking:{type:"enabled"|"disabled"}and does not understand OpenAI'sreasoning_effort/reasoning, while its request validator is strict (400 Param Incorrect). OmniRoute's OpenAI path carried reasoning intent asreasoning_effort, and the claude→openai translator can leave a Claude-shapedthinking:{type, budget_tokens}— so the client's on/off choice was silently dropped andbudget_tokens/reasoning_effortrode along as extra params the validator can reject. Newopen-sse/services/mimoThinking.ts::normalizeMimoThinking(wired inchatCoreforprovider==="xiaomi-mimo") reduces any thinking object to just{type}(disabledstays;enabled/adaptive/other →enabled) and dropsreasoning_effort/reasoning. It deliberately does not synthesize thinking from a barereasoning_effort—mimo-v2-omniis non-thinking, so that could turn a silently-ignored param into a hard error. (#4224)*-prochat models are text-only (no vision) — onlymimo-v2.5andmimo-v2-omniaccept images per Xiaomi's docs;mimo-v2.5-pro/mimo-v2-proare text-only, butmodelSpecsmarked them vision-capable and models.dev mislabels them (hermes-agent#18884). SinceresolveVisionCapabilitylets a syncedattachment:truewin first, an image request could be routed to a blind model (the fix(sse): route image requests only to confirmed-vision combo targets #4071 failure mode). Corrected the specs and added a hard override inresolveVisionCapability(checked before the synced branch, anchored somimo-v2.5-pronever matches the multimodalmimo-v2.5) that beats the wrong synced attachment. Also registered the missing nativemimo-v2-prochat model and the missingmimo-v2-ttsspeech model. (#4224)thinking.type:"enabled"or anythinking.budget_tokensnow returns400("Any request that tries to set a fixed thinking budget gets a 400" — Anthropic migration guide). Reasoning is adaptive-only, steered byoutput_config.effort. OmniRoute's OpenAI→Claude translator mappedreasoning_effortlow/medium/high to a manualthinking:{type:"enabled", budget_tokens}, so those requests hard-400'd on the most-used provider (and a Claude-native passthrough client sending the legacy shape did too). A newadaptiveThinkingOnlymodel flag now drives two fixes: the translator mapsreasoning_effortof every level to{type:"adaptive"}+output_config.effort(preserving the requested level, never a budget) for these models, and anormalizeClaudeAdaptiveThinkingcatch-all at the existing post-translation thinking-normalization chokepoint collapses any residual manual thinking (passthrough legacy shape, per-model defaults) to{type:"adaptive"}, keyed on the resolved upstream model so it covers every routing mode. Pre-4.7 models (Opus 4.6/4.5, Sonnet, Haiku) keep manual budgets unchanged. (#4230)temperature/top_p/top_kwith a400(sampling is fixed; reasoning moved tooutput_config.effort). The translator forwarded client-suppliedtemperature/top_punconditionally and the Claude registry models carried nounsupportedParams, so a plain OpenAI-format request withtemperature: 0.7toclaude-opus-4-8hard-400'd. AddedunsupportedParams: ["temperature","top_p","top_k"]to the Opus 4.7+/Fable 5 ids in both theclaude(dashedclaude-opus-4-8) andanthropic(dottedclaude-opus-4.7) registries, so they're stripped at the existinggetUnsupportedParamsdispatch chokepoint. Pre-4.7 Claude models still accept sampling params. (#4230)openaiChat Completions path (no 400 when an effort is active) — GPT-5 reasoning models reject non-defaulttemperature/top_pwith a400whenever a reasoning effort is active, yet accept them again underreasoning_effort:"none"(the GPT-5.1+ default, i.e. non-reasoning mode). On theopenaiprovider onlyo3carriedREASONING_UNSUPPORTED;gpt-5.5/gpt-5.4/gpt-5.4-mini/gpt-5.4-nanocarried no sampling guard, so atemperature+ active-effort request hard-400'd. A staticunsupportedParamslist can't express thenone-mode carve-out (it would over-strip the legitimate case), so the newgpt5SamplingGuarddropstemperature/top_ponly when the resolved effort is active — wired at the existinggetUnsupportedParamschokepoint and scoped to theopenaiChat Completions surface (thecodexResponses path is already covered by the CodexExecutor allowlist; other providers are untouched). (#4245)verbosity/text.verbosity) — the GPT-5 series added an output-verbosity control:verbosity(low/medium/high) on Chat Completions, nested astext.verbosityon the Responses API. The CodexExecutor gates translated requests through an allowlist that had notextentry, so for thecodexprovider the hint was dropped before reaching upstream (theopenaiChat path already forwarded it).normalizeCodexVerbositynow folds whichever shape arrived into a single validatedtext:{verbosity}before the allowlist (which now permitstext), and the OpenAI Chat↔Responses request translators mapverbosityacross formats so the hint survives a format crossing for non-codex Responses backends too. Invalid/absent verbosity collapses to notext(status quo). (#4245)reasoning_effortto DeepSeek V4's native{high, max}vocabulary — DeepSeek V4 only understandshigh/maxreasoning levels, so otherreasoning_effortvalues are mapped onto its native vocabulary instead of being rejected. (#4219)max_tokensand an extended timeout for GLM-5.2+ thinking — GLM-5.2+ thinking responses are slow and need headroom, so OmniRoute now sets a sensible defaultmax_tokensand a longer timeout for them. (#4255 — thanks @dhaern)includeThoughtsfor modern Gemini models — modern Gemini models on the Antigravity path now default to including thoughts so reasoning isn't silently dropped. (#4180 — thanks @dhaern)contextLengthto [provider removed at its operator's request] models — fills in accurate context-window sizes for [provider removed at its operator's request]'s models. (#4184 — thanks @herjarsa)/v1/modelsnow reports token limits for combo models. (#4189 — thanks @megamen32)mcpAccessibilitymaxTextCharson the live read path — the live read path now clampsmaxTextCharsso a small value can't make tools disappear. (#4206)next buildto stop OOM/stall — bumps the build-time heap so the local production build no longer OOMs or stalls. (#4171)hasFree, round the headline to ~1.6B, regenerate the per-provider table — drops dead free tiers from the headline math and regenerates the per-provider free-tier table. (#4142)🧪 Tests
_toolNameMapcloak map through the request-capture round-trip — follow-up to v3.8.27: MCP tool dispatch broken in Claude Code — tool schemas appear but every call returns "No such tool available" (works on 3.8.24) #4091: the generic capture fix increatePreparedRequestLogger().body()(fix(sse): restore MCP/third-party tool names on native Claude path (#4091) #4153) re-attaches the non-enumerable_toolNameMapthat the request-inspector drops when it rebuilds the upstream body viaJSON.parse(JSON.stringify(...)), but the only regression test covered the native-Claude OAuth cloak (PascalCase aliases). The Antigravity cloak differs —cloakAntigravityToolPayloadsuffixes custom tools with_ide(workspace_read→workspace_read_ide), leaves native tools untouched, and returns the reverse map separately — so a refactor ofproviderRequestLogging.tsor the executor could silently re-break Antigravity tool dispatch without tripping the Claude test. Adds a dedicated regression test driving the realcloakAntigravityToolPayloadthrough the capture round-trip and asserting the_idereverse map survives, stays non-enumerable (never re-serializes upstream), and that all-native traffic produces no spurious map (verified failing with the fix(sse): restore MCP/third-party tool names on native Claude path (#4091) #4153 re-attach removed). No production change. (#4181 — thanks @hertznsk)📝 Maintenance
combo/rrState.ts(D7a) — moves round-robin sticky state into its own module. (#4196)combo/quotaStrategies.ts(D7b) — moves the reset-aware quota strategies into their own module. (#4204)--strict(QG v2 Fase 9 T9) — tightens the anti-hallucination docs checker. (#4149)stryker -cis--concurrency); record Task 12 GO — corrects a misread of the stryker flag and records the spike GO. (#4138)/api/v1/wschat WebSocket endpoint in openapi.yaml — adds the WebSocket chat endpoint to the OpenAPI spec. (#4215)🔧 Dependencies
🙌 Contributors
Thanks to everyone whose work landed in v3.8.29:
<category>:<tier>combos), #4164 (auto/* in /v1/models)Quality Gate (release prep, run locally on
release/v3.8.29)Range:
v3.8.28..release/v3.8.29— 115 commits, all covered (119 changelog bullets).