[3.8.40] — TBD
In development — bullets added per PR; finalized at release.
✨ New Features
- feat(compression): relevance extractive engine — a new opt-in compression engine that scores each sentence by term-overlap (Jaccard) with the user's last query minus a length/boilerplate penalty, greedily keeps the most relevant within a budget, and reconstructs the original order. Pure-string, deterministic, ReDoS-safe (char-code tokenization, no
RegExpover user input), fail-open, default off. Ideal for trimming long pasted RAG context / tool output to what's relevant. Sentences carrying real signal (digits/URLs/errors/code/paths) are never dropped;overlapThreshold/budgetPercent/boilerplateWeightare configurable. Tier-2 item of the compression feature-extraction roadmap (#7). (#5289) - feat(compression): hard-budget mode — compress to ≤ N tokens — a deterministic post-pass (
targetTokens/targetRatio, default unset → no-op) that trims a body to a token budget. It ranks sentences/lines by averagescoreTokenascending and drops the lowest-saliency ones until the body fits (measured by the exact cl100kcountTextTokens), preserving original order. Lines carrying real signal (digits, URLs,Error:-family, code fences, stackat-frames, multi-segment paths,key=value) are never dropped; the budget is distributed proportionally across messages so the total stays ≤ target; an unreachable target (all-preserved) surfaces avalidationWarningsnote instead of failing silently. Does NOT touch theestimateCompressionTokensbudget-gate estimator. Tier-3 item of the compression feature-extraction roadmap (#17). (#5288, follow-up #5291) - feat(compression): result memoization for deterministic engines (opt-in) — caches
(input, config) → resultfor provably pure, stateless modes (lite/standard/rtkand stacked pipelines of{lite,caveman,rtk}) to skip recompute on the hot path. Opt-in viamemoizeCompressionResults(default off → zero behavior change). Conservative opt-in whitelist (statefulccr/session-dedup— which write the cross-request CCR store — and model-backedultra/aggressive/llmlinguaare never cached), principal-scoped (skipped without a principal, so no cross-principal body leak), and clone-on-store + clone-on-read. Tier-3 item of the compression feature-extraction roadmap (#21). (#5286) - feat(compression): inline transparency annotation — surfaces
tokens=847→312; rules: filler×8, dedup×2derived from existing compression stats. TheX-OmniRoute-Compressionresponse header is extended append-only (themode; source=Xprefix stays byte-identical, so existing header parsers don't break) and the compression studio cockpit shows a matching badge. Zero new computation — it aggregates therulesApplied/techniquesUsedalready on the stats. Tier-3 item of the compression feature-extraction roadmap (#18). (#5284) - feat(compression): saliency heatmap in the compression studio — the preview studio can now color each token by saliency:
ultraper-tokenscoreToken(0–1, green→red gradient) or universal kept/removed from the existing diff. A dry-run visualization behind a toggle (no cost on a normal preview; backward-compatible when off). Completes the visualization half of roadmap item #13 (the A/B comparison shipped in #5080). (#5285) - feat(compression): composite-command splitter for RTK detection —
cd /x && git statusnow detects asgit-status(previously the whole string was treated as one command and matched no filter). A quote-aware top-level tokenizer splits on&&/||/;(never inside quotes or$(…)/backtick subshells) and feeds the last segment to RTK command detection, so every RTK filter/renderer fires on commands wrapped incd … &&/||/;chains. O(n), no RegExp over the command (ReDoS-safe). Tier-3 item of the compression feature-extraction roadmap (#16). (#5283) - feat(mcp):
omniroute_tool_searchtool + one-line TS signatures — new MCP tool that does lexical keyword search over every MCP tool's name/description and returns the top matches as compact one-line TypeScript signatures (~half the JSON-schema token cost), so agents discover tools on demand instead of carrying all ~88 schemas every turn. Search is ReDoS-safe (substring scoring, nevernew RegExpon the query) and deterministic;tools/liststays complete (no hidden tools). Adds theread:toolsscope. Tier-1 item of the compression feature-extraction roadmap. (#5269) - feat(compression): RTK semantic command-output renderers (opt-in) — adds a second, opt-in compaction layer to the RTK engine that rewrites structured command output into a far more compact semantic form:
git diff→ file headers +@@hunks + changed lines only; an all-greenpytest/jest/vitest/eslintrun → its one-line summary;terraform/tofu plan→Plan: +N ~M -Kplus the resource list;kubectl/awsJSON arrays → a minimal table. Each renderer is conservative (no-op when the shape doesn't match) and the integration is fail-open; the test-green renderer never collapses output that carries any failure signal. Gated byRtkConfig.enableRenderers(default off → zero behavioral change). Eighth item of the compression feature-extraction roadmap. (#5268) - feat(compression): QuantumLock cache-prefix stabilization (opt-in, default off) — recovers upstream prompt-cache hits that a volatile fragment in the system prompt would otherwise bust. When a caller injects a session UUID, unix timestamp, request-id, JWT, API-key shape, or long hex digest into the
role:systemmessage every turn, the longest common prefix across turns ends at that changing byte → the whole system prompt after it is re-billed and re-processed each turn. QuantumLock replaces each non-semantic volatile fragment with a positional, value-independent placeholder⟦Q{i}⟧and appends the real values in a delimited⟦QUANTUMLOCK⟧tail. The rewrite is sent to the model (lossless — not restored), so the system-prompt body becomes byte-identical across turns and the provider caches the long stable prefix while only the small tail differs. Opt-in, default off, applied only for caching providers (isCachingProvider && config.quantumLock.enabled); bounded ReDoS-safe patterns; idempotent; no date/time patterns (semantically meaningful — explicit non-goal). Studio gets a toggle + a "🔒 N volatile fragment(s) stabilized" dry-run badge. Seventh item of the compression feature-extraction roadmap (bench: #5080, gate: #5127, fuzzy: #5143, ionizer: #5148, TOON: #5163, CCR ranged: #5187, risk-gate: #5243). (#5260) - kilocode: anonymous (no-auth) access to Kilo Code's free models, mirroring the
opencode/mimocodepattern. With no Kilo account connected, requests now fall back to the gateway's anonymous tier (Authorization: Bearer anonymousonapi.kilo.ai/api/openrouter) so the free models work without signup; a connected OAuth account is still used unchanged for the paid tier (#5259, #4019 — thanks @Theadd for the reference implementation) - feat(logging): call-log correlation ID (end-to-end) — every request now gets a unique correlation id, returned in the
X-Correlation-Idresponse header, persisted incall_logs(migration 109), filterable via/api/usage/call-logs, and surfaced in the dashboard request logger (per-chunk stream timestamps + active-requests-first sort). This is the safe, cohesive core subset of the larger #5275 — landed on its own so the low-risk value isn't blocked by the parts of that PR still under review. (#5279 — thanks @hartmark) - feat(providers): Microsoft 365 Copilot individual provider — adds the
copilot-m365-webprovider (the 237th), wiring the M365 BizChat framing/connection helpers into a selectable web-session provider backed bym365.cloud.microsoft/chatfor individual Microsoft 365 plans. Builds on the M365 pure-framing groundwork from #4696. Regression guard:tests/unit/copilot-m365-web-executor.test.ts. (#5302 — thanks @skyzea1)
🔧 Bug Fixes
- ci(docker): re-point the Docker Hub / GHCR
:latest(and:latest-web) tags to the just-published release. On arelease: releasedevent the freshly-created git tag is often not yet visible togit fetch --tagswhendocker-publishruns, so the:latest-promotion gate built its candidate set purely fromgit tag -land resolved the highest semver to the previous version — leavinglatestone release behind (3.8.39 published,lateststill 3.8.38). The decision now lives inscripts/ci/should-promote-latest.sh, which folds the currentVERSIONinto the candidate set before picking the highest stable semver, making promotion independent of tag-sync timing (a patch published after a higher minor still won't grablatest). Regression guard:tests/unit/build/should-promote-latest-5301.test.ts(#5301) - command-code: treat a non-positive
max_tokens/max_completion_tokens(e.g. Zoo Code's-1"let the server choose") as "no limit" — omit the field instead of forcing it to1.clampMaxTokenspreviously didMath.max(1, …), so a client-1was sent upstream asmax_tokens: 1, truncating the response to a single token (the observedcompletion_tokens: 1,content: null,reasoning_content: "The"withfinish_reason: stop). Now any value≤ 0is dropped so Command Code applies the model's native default; positive values are still floored and clamped to the 200k ceiling. Regression guard:tests/unit/command-code-maxtokens-negative-5166.test.ts(#5166 — thanks @Stazyu) - fix(auth): compare-and-swap guard on the OAuth refresh persist — under multi-agent load, the per-connection refresh mutex makes
[network refresh + DB write]atomic for one connection, but it does not protect against a third writer (a sibling request, a concurrent HealthCheck, or a replica) landing a fresherrefresh_tokenrotation on the sameconnection_idbetween the staleness read and the persist. Overwriting that fresher row reverts the sibling's rotation; the next caller then loads the now-consumed token, Auth0/Anthropic flag it asrefresh_token_reused, and the whole token family gets revoked (the 1352× claude/aa5dd5cfinvalidation storm).getAccessTokennow re-reads the row's currentrefresh_tokenimmediately before persisting (inside the mutex) and skips the write when it has rotated past the token the caller presented — the caller still receives the freshly-issued access token, only the DB overwrite is skipped. Opt-in viarunWithCasGuard(no active guard ⇒ byte-identical behavior); skip/persist counters exposed viagetCasGuardStats(). Regression guard:tests/unit/token-refresh-cas-guard-4038.test.ts. (#4038 — thanks @KooshaPari for the root-cause diagnosis) - mcp: break the
schemas/tools.ts ↔ schemas/toolSearch.tsimport cycle introduced when thetool_searchdefs (#5269) were extracted into their own module —toolSearch.tsimportedMcpToolDefinitionfromtools.tswhiletools.tsimportedtoolSearchToolfromtoolSearch.ts, failingcheck:cyclesonrelease/v3.8.40. The sharedAuditLevel+McpToolDefinitiontypes now live in a leafschemas/toolDefinition.tsthat both import;tools.tsre-exports them for backward compatibility. - compression (analytics): record attempted-but-no-op compression runs so Stacked is no longer invisible when it saves nothing. Previously a
compression_analyticsrow was written only on a net-positive saving, so a Stacked (RTK→Caveman) pipeline that ran on already-compact context produced no row — indistinguishable from "never dispatched" (byMode.stacked.countstayed flat while Ultra climbed). Such runs are now recorded withskip_reasonand surfaced as a per-modeskippedcount plustotalSkipped/bySkipReasonin the analytics summary and the Mode Breakdown; the existing net-saving totals/averages are unchanged (skip rows are excluded from them) (#4268 — thanks @abdulkadirozyurt, @androw) - cli (tray): fix
omniroute server --trayshowing no tray on macOS/Linux with no error printed. The wired Unix tray path loadedsystray2through an inline loader that calledrequire("module")inside an ESM.mjsfile ("type":"module") →ReferenceError: require is not defined, silently swallowed (regressed in v3.8.34); even if it had loaded,systray2isn't innode_modules(it's lazily installed into~/.omniroute/runtime). The loader now delegates to the runtime loader, the icon path (icon.png) is corrected,isTemplateIconisfalse(the full-color icon rendered as a white square under macOS template mode), and tray start failures are surfaced to stderr instead of being swallowed (#4605 — thanks @ProgMEM-CC) - agent-bridge (antigravity): unwrap the cloudcode-pa
.requestenvelope when converting Antigravity IDE requests. The real IDE sendscloudcode-pa.googleapis.com/v1internal:generateContentwith the Gemini request nested under.request({ project, model, request: { contents, systemInstruction, generationConfig } }), but the bridge read those fields at the top level — yielding an empty conversation, so prompts hung mid-execution. The legacy/v1beta/models/<model>:generateContenttop-level shape still works (#4294 — thanks @shabeer) - dashboard: add a GitHub releases fallback to the "Update Available" lookup. After the v3.8.28 fix added an npm-registry HTTP fallback, the banner could still stay hidden on networks that reach GitHub (where the news feed already loads) but not
registry.npmjs.org.resolveLatestVersion()now tries npm CLI → npm registry → GitHub releases (/repos/diegosouzapw/OmniRoute/releases/latest) before giving up, and logs a warning only when all three fail (#4100) - command-code: omit
max_tokenswhen the client omits it so the upstream applies the model's native default, fixing400 "expected <=200000"on/alpha/generatefor high-cap models; an explicit oversized client value is clamped to the 200k endpoint ceiling (#5221 — thanks @adivekar-utexas) - combo: wire session stickiness into the round-robin dispatch path. Multi-turn conversations from clients that send no session id (Codex CLI, Claude Code, most OpenAI-compatible tools) were rotated to a different connection on every turn by round-robin combos, busting the upstream prompt-cache → cold high-reasoning starts, intermittent
504s and throughput collapse under concurrency. The weighted/priority paths already honored per-conversation stickiness; the round-robin handler returned before reaching it. Round-robin now starts the rotation at the conversation's sticky connection (failover to the other targets is preserved), and different conversations still spread across connections — only intra-conversation rotation is removed (#5248, #3825 — thanks @bypanghu, @jpsn123, @xz-dev) - kiro: replace the synthesized trailing
"Continue"turn with a neutral filler ("...") — when an OpenAI→Kiro request ends on an assistant/tool turn, the translator synthesizes the protocol-required trailing user turn, and the literal word"Continue"could be read by Kiro/CodeWhisperer as a real user instruction and trigger unintended agent action. A trailing tool-result turn is still promoted as-is (it already collapses to a real user turn); only the assistant-text-ending case is affected. Regression guards:tests/unit/kiro-continue-filler-5231.test.ts. (#5231) - combo: advance to the next combo target on a
400 "requested model is not supported"instead of hard-failing. The 400 guard in the priority strategy treatedMODEL_CAPACITYas a block-fallback reason, so a combo that hit a provider lacking a specific model returned a hard400even when other targets (different providers) supported it. Such 400s now fall through to the next target. (#5249 — thanks @Chewji9875) - dashboard: disabled no-auth providers no longer vanish from the All Providers page. Disabling a no-auth provider (the "No authentication required" toggle, which adds it to
blockedProviders) silently removed its card because the page dropped blocked no-auth entries from its render list — the only way back was buried under Settings → Security → Blocked Providers. The page now partitions no-auth entries: visible providers render as before, blocked ones appear in a "Disabled" sub-group with an Enable button that un-blocks them in place. Aggregates, counts and/v1/modelsstill consume the visible-only list (blocked providers stay out of routing). Regression guard:tests/unit/noauth-blocked-partition-5183.test.ts. (#5183, follow-up from #5166 — thanks @WslzGmzs) - dashboard: add a parent
/dashboard/contextpage so RSC prefetches of the compression-context hub no longer 404. The route only had sub-routes (settings,combos,ultra, …) and no parent page, so the App Router returned 404 for the bare segment. The parent now redirects to its canonical sub-route (/dashboard/context/settings), honoring a legacy?tab=query for deep links. Regression guard:tests/unit/dashboard/context-parent-redirect-5298.test.ts(#5298 — thanks @KooshaPari) - i18n: add the missing
sidebar.gamificationGroupmessage across all 42 locales — the Gamification sidebar group referenced atitleKeythat existed in no locale, loggingMISSING_MESSAGE: sidebar.gamificationGroup (en)at runtime (the group still rendered via itstitleFallback). The key is now present everywhere so the warning is gone and locale coverage is unaffected (#5298 — thanks @KooshaPari) - api(stream):
/v1/chat/completionsno longer returns SSE for a non-stream OpenAI-compatible request whenstreamis omitted and the client sendsAccept: application/json, text/event-stream— the Vercel AI SDK / OpenAI SDK non-stream signature (doGenerate()/generateText()), which then failed withInvalid JSON response(Unexpected token 'd', "data: {"id"...). The route-level Accept override (#302) andresolveStreamFlagnow treat an Accept header that explicitly listsapplication/jsonas a JSON opt-in even when it also liststext/event-stream; only a pureAccept: text/event-stream(noapplication/json) still opts an omitted-streamrequest into SSE, and an explicit bodystreamvalue always wins. The shared decision now lives inacceptHeaderForcesStream. Regression guard:tests/unit/sse-nonstream-accept-5305.test.ts. (#5305 — thanks @md-riaz) - providers: drop the retired GPT‑5.2 / GPT‑4.5 models from the direct ChatGPT‑web and Codex surfaces (OpenAI removed them there), so OmniRoute stops advertising/routing models that no longer exist. Scoped on purpose to those two providers — third‑party proxies that still expose the ids are untouched. (#5280 — thanks @backryun)
- codex: drop the deprecated
local_shellhosted tool type before forwarding to OpenAI's Responses API, resolving the omni-combo400 "The local_shell tool is no longer supported."spike. Inbound Responseslocal_shellis still accepted and mapped to a caller-side Chatshellfunction for compatibility. (#5250, #5256 — thanks @KooshaPari) - antigravity: retry excluded accounts via the fallback LRU. The combo same-model retry loop accumulates excluded Antigravity connection ids after account-level failures, but auth selection only treated a single
excludeConnectionIdas a fallback scenario — once exclusions accumulated throughexcludedConnectionIds, selection could fall back to normal sticky/priority behavior instead of LRU-selecting the next eligible account for the same model/family. Any non-empty accumulated exclude set is now treated as fallback mode. Builds on the family-scoped lockout work in #5180 (v3.8.39). (#5222 — thanks @Ardem2025) - grok-cli: strip unsupported sampling params (
presencePenalty,frequencyPenalty,logprobs,topLogprobs) before sending to the Grok Build API, fixing400 'Model does not support parameter presencePenalty'when clients (MiMoCode, Cursor, etc.) send OpenAI-style params. (#5273 — thanks @fulorgnas) - grok-cli: accept the full
~/.grok/auth.jsonobject in the dashboard import-token endpoint. TheoauthImportTokenSchemaonly accepted a bare string token while the UI sends the whole auth.json object →400 Bad Request; the schema now accepts the object and stores the original underproviderSpecificData.rawAuthJsonfor diagnostics and token refresh. (#5258 — thanks @fulorgnas) - qoder: coalesce concurrent PAT→job-token exchanges per PAT so high-concurrency / multi-agent bursts no longer stampede
openapi.qoder.sh/api/v1/jobToken/exchangebefore the first exchange populates the completed-token cache; the shared exchange is also decoupled from any single caller'sAbortSignalso one aborted waiter can't cancel it for the others. (#5254, #5265 — thanks @KooshaPari) - proxy: scope the fallback reachability cache by normalized target URL instead of by hostname, so a failed probe for one endpoint on a shared API host no longer suppresses a later probe for a different endpoint on that same host for the full TTL (host fallback is preserved for malformed URLs). (#5261 — thanks @KooshaPari)
- proxy: cache failed fast-fail health probes with a short negative TTL instead of the full positive health TTL, so a single transient timeout/load blip no longer marks a working residential SOCKS5 proxy unreachable for the whole window (#5109 regression coverage added). (#5255 — thanks @KooshaPari)
- mcp: forward HTTP auth to internal tool fetches so MCP tools that call back into the local API surface carry the caller's authorization. (#5218 — thanks @KooshaPari)
- logging: preserve the outbound provider request headers in the detailed call-log Provider Request payload (previously the upstream response headers were shown there). chatCore now keeps executor-returned request headers when wrapping streaming and non-streaming responses; response headers stay scoped to the
Response. (#5257 — thanks @rdself) - sse: scope textual
<think>/<thinking>tag extraction so generic OpenAI-compatible paths don't rewrite prompt-format content intoreasoning_content; an explicit opt-in keeps tag-native families (DeepSeek-R1, QwQ) working while Antigravity/Agy stay excluded by provider/model prefix. (#5224 — thanks @rdself) - mcp: break the
schemas/tools.ts ↔ schemas/toolSearch.tsimport cycle introduced when thetool_searchdefs (#5269) were extracted into their own module —toolSearch.tsimportedMcpToolDefinitionfromtools.tswhiletools.tsimportedtoolSearchToolfromtoolSearch.ts, failingcheck:cyclesonrelease/v3.8.40. The sharedAuditLevel+McpToolDefinitiontypes now live in a leafschemas/toolDefinition.tsthat both import;tools.tsre-exports them for backward compatibility. (#5282) - compression (analytics): record attempted-but-no-op compression runs so Stacked is no longer invisible when it saves nothing. A
compression_analyticsrow was previously written only on a net-positive saving, so a Stacked pipeline that ran on already-compact context produced no row — indistinguishable from "never dispatched". Such runs are now recorded withskip_reasonand surfaced as a per-modeskippedcount plustotalSkipped/bySkipReason; net-saving totals/averages are unchanged (skip rows excluded). (#5277, #4268 — thanks @abdulkadirozyurt, @androw) - cli (tray): fix
omniroute server --trayshowing no tray on macOS/Linux with no error printed. The Unix tray path loadedsystray2through an inline loader that calledrequire("module")inside an ESM.mjsfile →ReferenceError: require is not defined, silently swallowed (regressed in v3.8.34); even if loaded,systray2is lazily installed into~/.omniroute/runtime, notnode_modules. The loader now delegates to the runtime loader, the icon path is corrected,isTemplateIconisfalse(the full-color icon rendered as a white square under macOS template mode), and tray start failures surface to stderr. (#5276, #4605 — thanks @ProgMEM-CC) - agent-bridge (antigravity): unwrap the cloudcode-pa
.requestenvelope when converting Antigravity IDE requests. The real IDE sendscloudcode-pa.googleapis.com/v1internal:generateContentwith the Gemini request nested under.request, but the bridge read those fields at the top level — yielding an empty conversation, so prompts hung mid-execution. The legacy/v1beta/models/<model>:generateContenttop-level shape still works. (#5267, #4294 — thanks @shabeer) - dashboard: add a GitHub releases fallback to the "Update Available" lookup. After the v3.8.28 npm-registry fallback, the banner could still stay hidden on networks that reach GitHub but not
registry.npmjs.org.resolveLatestVersion()now tries npm CLI → npm registry → GitHub releases before giving up. (#5266, #4100) - command-code: omit
max_tokenswhen the client omits it so the upstream applies the model's native default, fixing400 "expected <=200000"on/alpha/generatefor high-cap models; an explicit oversized client value is clamped to the 200k endpoint ceiling. (#5221 — thanks @adivekar-utexas)
🔒 Security
- authz: require auth for the
/v1beta/*Gemini-compatible client API.next.config.mjsrewrote/v1beta/:path*→/api/v1beta/:path*, butsrc/proxy.tsdidn't match/v1betabefore the rewrite andclassifyRoute()didn't classify/api/v1beta/*as client API — so unauthenticated/v1beta/models/...:generateContenttraffic could reach the model-serving route without the central client-API auth policy. Both alias and rewritten forms are now classifiedCLIENT_API, enforcing Bearer auth whenREQUIRE_API_KEYis enabled. (#5274 — thanks @rdself) - sentinel: security hardening pass across request handling. (#5241 — thanks @iamedwardngo)
- providers: refresh impersonation User-Agents + TLS fingerprint profiles to current real-client versions; several had drifted or were inconsistent across files, a bot-detection/blocking risk. (#5237 — thanks @backryun)
- authz (public origin): centralize browser-mutation origin validation into
src/server/origin/publicOrigin.tsand wire it through the authz pipeline, replacing the per-route same-origin-only check that403'd dashboard mutations when served behind a reverse proxy on a different public origin. The module resolves the allowed public origin from configured base-URL env vars or trusted forwarded headers (only whenOMNIROUTE_TRUST_PROXYis set and the peer is loopback/LAN via peer-stamp), validatesSec-Fetch-Sitemetadata, and sanitizesHost/Forwardedinputs (rejects control chars, userinfo, path/query in Host). Regression guards:tests/unit/authz/public-origin.test.ts+tests/unit/authz/pipeline.test.ts. (#5278 — thanks @Thinkscape / @abodera)
📝 Maintenance
- docs: reorganize
docs/, run an accuracy audit, and drop Node 20 from the supported matrix (rebased onto the current release tip). (#5262) - providers: remove the discontinued Gemini CLI channel — Google shut it down on 2026-06-18; the supported migration path is Antigravity. (#5246 — thanks @rdself)
- dashboard: add more provider icons from Lobehub. (#5220 — thanks @backryun)
- deps: resolve
npm installwarnings, fix a runtimeCannot find modulecrash onomniroute serve(the runtime-env module was missing from the npmfilesallow-list), and clear the 4 moderatenpm auditadvisories. (#5252 — thanks @yunaamelia), (#5230, #5227) - docker: harden the base image against container-scan CVEs and skip comment lines in the #4076 builder heap-ordering check. (#5229, #5233)
- ci: Trivy advisory scan now ignores unfixed CVEs to cut Security-tab noise. (#5235)
- test(combo): reconcile the #4279 stop-guard test with the #5249 advance policy. (#5300)
- test(targets): add 14 unit tests for the shared
combo/targetExhaustion.tshandler (provider-exhausted / connection-error / transient rate-limited classification), registered in the vitest discovery list. (#5296 — thanks @KooshaPari)
What's Changed
- chore(docker): harden base image against container-scan CVEs by @diegosouzapw in #5228
- chore(ci): Trivy advisory scan ignores unfixed CVEs (Security-tab noise) by @diegosouzapw in #5234
- fix: centralize public origin checks for proxied dashboards by @Thinkscape in #5278
- Release v3.8.40 by @diegosouzapw in #5225
Full Changelog: v3.8.39...v3.8.40