Bump 3.8.11 → 3.8.12 across package.json, lockfile, electron/, open-sse/, and
docs/reference/openapi.yaml; add the [3.8.12] cycle placeholder to the root
CHANGELOG and the 41 i18n mirrors. Integration branch for the v3.8.12 cycle —
fixes/features land here via per-issue PRs and it merges to main at release time.
CodeQL flagged 7 high alerts in code the cycle touched (the large release-PR diff
re-surfaces them). Resolved at the source — no dismissals:
- fix(images): resolveImageBaseUrl trimmed trailing slashes with `/\/+$/`, a
polynomial-ReDoS pattern (js/polynomial-redos) on the configured node base URL.
Replace it with a non-backtracking endsWith/slice loop.
- test(oauth): pin the Anthropic OAuth host with exact-equality asserts and a
parsed-hostname negative check instead of substring `.includes()`
(js/incomplete-url-substring-sanitization). The exact-equality assertions were
already present, so coverage is unchanged.
- test(images): drop the redundant `!includes("generativelanguage.googleapis.com")`
assert — the exact-equality assert on the resolved URL already guarantees it.
Clear the two release-gate failures the CI Lint+Build jobs surfaced (release-branch
drift — PR merges bypassed pre-push):
- fix(api): /v1/images/edits parsed request.json() without a Zod guard
(route-validation t06 / hard rule #7). Add ImageEditJsonSchema.safeParse so a
malformed body (non-object / wrong types) is rejected with 400 instead of
silently parsed; valid JSON/data-URL bodies behave exactly as before. (#3214, #3215)
- fix(dashboard): remove a duplicate handleToggleProxyEnabled /
handleTogglePerKeyProxyEnabled / handleDistributeProxies block in
providers/[id]/page.tsx — a bad merge of the proxy PRs declared all three twice,
breaking the webpack build ("Identifier already declared"). The removed copy was
byte-identical to the kept one. (#3170, #3171, #3172)
Finalize the 3.8.11 cycle CHANGELOG and clear the failures the full test:unit
gate surfaced (release-branch drift — PR merges bypassed pre-push):
- CHANGELOG: date the [3.8.11] section (2026-06-05) + repo-housekeeping roll-up
- docs(env): document THEOLDLLM_NAV_TIMEOUT_MS in .env.example + ENVIRONMENT.md
(env-doc-sync gate; #3217 added the var without docs)
- test(nvidia): exercise the #3226 bypass-fetch path via a local HTTP server and
hoist the validator import (patching globalThis.fetch no longer intercepts the
un-patched native fetch captured by proxyFetch)
- test(i18n): import the shipped normalizeComplianceEventTypes helper (#3185)
- test(model-caps): save synced metadata under the canonical gemini-3.1-pro key
now that #3229 aliases gemini-3.1-pro-high/-low to it
- test(web-session): expect the grok-web "sso + sso-rw" credential hint (#3180)
- test(synced-models): isolate DATA_DIR so the #3199 hidden-override stops
bleeding into the shared DB and breaking the re-run precondition
agy's gemini-3.1-pro-high/-low had no alias, so resolveAntigravityModelId sent the
speculative -high/-low suffix verbatim to upstream, which rejects it (400) for
gemini-3.x. Worse, the non-stream executor branch fed the 4xx response into the SSE
collector, returning a synthetic empty {object:chat.completion} envelope that masked
the error. Alias both to gemini-3.1-pro, and surface real upstream errors via
buildErrorBody for non-ok non-stream responses. + unit tests.
The Codex CLI WS->HTTP fallback rewrite (resolveResponsesApiModel) prefixes a
bare model id with codex/ whenever codex/<id> resolves to codex. Codex accepts
arbitrary model strings, so a combo name with no slash (e.g. n8n-text,
paid-premium) was rewritten to codex/<combo> and sent to Codex instead of being
resolved as a combo — regressing combos via /v1/responses in v3.8.9+. Skip the
rewrite when the bare id is a combo (getComboByName). + unit test.
- grok-web: the credential hint named only "sso" while Grok needs both "sso"
and "sso-rw"; users pasted just sso and hit anti-bot 403s. Name both
(credentialName + placeholder). The underlying Cloudflare anti-bot 403 is
upstream and tracked separately on #3180.
- vertex: the Service Account JSON placeholder was an untranslated stub literal
("Vertex Service Account Placeholder") in 40 locales, making the field look
broken even though SA-JSON auth is fully supported. Replace with real
instructional text (zh localized; pt-BR already translated).
Guard test pins both hints.
The NVIDIA key-validation chat probe used models[0] (z-ai/glm-5.1), which
requires the 'Public API Endpoints' account permission and has DEGRADED
windows. Accounts lacking that permission see the probe hang until the
validation timeout, surfacing as a misleading 'Upstream Error' on a valid key.
Probe the universally-available meta/llama-3.1-8b-instruct instead, still
overridable via providerSpecificData.validationModelId. + unit test.
getProviderConfig falls back to { name: providerId } for unknown ids, so the
label precedence config.name || p.name let the raw internal UUID of a custom
provider shadow the friendly name HomePageClient already resolved into p.name.
Extract resolveTopologyNodeLabel (entry name first) + unit test.
Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".
/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
fields the multipart reader produces (was "Invalid multipart body").
The chatgpt-web conversation-continuation edit flow is unchanged.
The db-backups import route statically imported better-sqlite3, which is
stripped from the Next standalone server's node_modules in the packaged
Electron app. Loading the route then crashed with "Cannot find module
'better-sqlite3'" on the Windows installer, even though node:sqlite was
available. Route the upload integrity-check through openDatabaseAsync
(better-sqlite3 -> node:sqlite -> sql.js), matching every other DB path.
Adds a guard test so no API route can reintroduce a direct native import.
Follow-up to #3204: deleting a synced (fetched) model removed it from the
synced set, but the DELETE route didn't mark it hidden and the re-import path
didn't skip hidden ids, so the next auto-fetch re-added it. Now the route
marks the id hidden on delete and replaceSyncedAvailableModelsForConnection
filters hidden ids, so the deletion sticks.
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
Residual of #3136: a local connection with an empty baseUrl still resolved
to this.config.baseUrl (OpenAI). Fall back to the provider's localDefault
(127.0.0.1:8080/v1) before the OpenAI default for the local-provider group.
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
The healthcheck probed only 127.0.0.1 and swallowed every error (empty
Output), so containers binding to a non-loopback address always reported
unhealthy with no diagnostic. Add a multi-host probe helper that succeeds on
the first 2xx and prints the last error to stderr on total failure.
Co-authored-by: naimo84 <naimo84@users.noreply.github.com>
The image-generation handler read credentials.baseUrl (always undefined),
so custom OpenAI-compatible image providers fell back to the Gemini endpoint
(401). Resolve from providerSpecificData.baseUrl like the chat path, and
rewrite prefix/model to the internal node id before the exact-id lookup.
Co-authored-by: ngocquynh85 <ngocquynh85@users.noreply.github.com>
normalizeDiscoveredModels only copied record.inputTokenLimit, but OpenRouter
returns the window as context_length / top_provider.context_length, so every
synced model fell back to the 128K default. Read context_length (and
top_provider.max_completion_tokens for output) as a fallback.
Co-authored-by: pulyankote <pulyankote@users.noreply.github.com>
#3185 (zhiru) and #3167 (androw / Nicolas Lorin) independently fixed the same
next-intl dotted-key bug. #3185 shipped (runtime normalize); #3167 is being
closed as superseded. Per the repo's contributor-credit policy, record the
parallel credit in the release notes so androw is co-credited even though their
PR is not the one that merged.
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>