The Agent Bridge page seeded a well-shaped initialData default then replaced it
wholesale with the raw /api/tools/agent-bridge/state response. The route returns
{ server, agents } but the UI reads { serverState, agentStates, bypassPatterns,
mappings }, so serverState became undefined and AgentBridgeServerCard crashed on
serverState.running — surfaced as the full-page 'Internal Server Error' boundary
(client render error, not a real 5xx).
Add a shared normalizeAgentBridgeState() that maps the route shape into the page
contract (server.running/certExists -> serverState) and always returns safe
defaults (never undefined serverState). Wired into both the SSR loader (page.tsx)
and the polling hook. The legacy 'agents' entry shape differs from AgentStateEntry
so it is not coerced; full route<->page contract reconciliation (port, upstreamCa,
bypassPatterns, mappings, agentStates) is a follow-up.
Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com>
no-auth providers (opencode, duckduckgo-web, theoldllm, veoaifree-web) never
create a DB connection row so stats.total stays 0, which the configured-only
filter treated as 'unconfigured' and hid them — even though they are always
usable and appear unconditionally in /v1/models. filterConfiguredProviderEntries
now treats displayAuthType === 'no-auth' as configured.
Co-authored-by: uniQta <uniQta@users.noreply.github.com>
- chipotle/grokTls: explicit null checks instead of a Promise in a boolean
conditional (behavior-preserving; clears the 2 MAJOR reliability bugs)
- sqliteQuotaStore.poolUsage: drop the unreachable dimMap scan loops (dimMap
was never populated) — the lightweight snapshot already returns no
dimensions; poolUsageWithDimensions() is the plan-aware path
- BudgetTab: presentation role + keyboard handler on the checkbox wrapper
Integrated into release/v3.8.12. Salvaged the emitHookBlocking payload-chaining fix from the now-closed plugins-v4 branch (#3221) and adapted it to the shipped release hooks.ts: each blocking handler now sees the body/metadata as mutated by previous handlers. TDD regression test included (RED before, GREEN after); existing plugins-hooks suites green (19+5), typecheck + lint clean.
Follow-up to the #3269 private-webhook opt-in. With the opt-in on, the private-host
check was bypassed entirely, leaving cloud-metadata endpoints (169.254.169.254,
metadata.google.internal, 100.100.100.200, link-local 169.254.0.0/16) reachable — the
classic SSRF -> IAM-credential pivot — and the webhook test endpoint returned the
upstream body, making it a content-exfiltration primitive against internal services.
- outboundUrlGuard: add isCloudMetadataHost(); parseAndValidateWebhookUrl blocks those
hosts UNCONDITIONALLY, even when private targets are opted in.
- webhooks/[id]/test: redact responseBody for private targets (status + latency only).
Regression test: tests/unit/webhook-metadata-guard-3269.test.ts (RED before, GREEN after);
existing webhook SSRF/opt-in suites stay green (34/34).
Webhooks hardcoded parseAndValidatePublicUrl, which blocks any RFC1918/loopback host —
breaking self-hosted setups that legitimately point webhooks at internal services
(n8n, Home Assistant, a LAN box). Provider URLs already had an opt-in
(OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS); webhooks now reuse it.
- outboundUrlGuard: add parseAndValidateWebhookUrl — gates the private-host check on
arePrivateProviderUrlsAllowed() (default OFF); protocol + embedded-credential checks
stay unconditional.
- swap all webhook call sites (create/update/test/validate-url + dispatcher x2) to it.
Regression test: tests/unit/webhook-private-optin-3269.test.ts (RED before, GREEN after);
existing webhook SSRF/dispatcher suites stay green (33/33).
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)
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>
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>
* chore(release): open v3.8.10 development cycle
Bump 3.8.9 → 3.8.10 across package.json, lockfile, electron, open-sse, and
docs/reference/openapi.yaml; add the [3.8.10] CHANGELOG section (root + 41 i18n
mirrors) as the integration target for the cycle. Entries land here as work
merges into release/v3.8.10; finalized by the release flow.
* fix(providers): resolve web provider alias collisions
Assign unique aliases to HuggingChat, Kimi Web, and Qwen Web so they no longer shadow primary providers or trigger startup warnings.
Add a unit test to enforce provider alias uniqueness and prevent future collisions. Also expand local ignore and VS Code exclude rules for agent, build, and worktree artifacts.
* fix(responses): normalize image_url parts across input paths (#3150)
Normalize image_url parts across all Responses input paths. Integrated into release/v3.8.10.
* fix(api-manager): preserve API key expiration local time (#3146)
Preserve API key expiration local time + clear button. Integrated into release/v3.8.10.
* Strip previous_response_id for stateless Responses upstreams (#3143)
Strip previous_response_id for stateless Responses upstreams (auto/strip/preserve). Integrated into release/v3.8.10.
* fix(opencode-plugin): map thinking cap to interleaved in model+combo (#3138)
Map caps.thinking to ModelV2.capabilities.interleaved for opencode-plugin. Integrated into release/v3.8.10.
* fix(providers): use synced models as fallback for all providers (#3148)
Use synced models as authoritative local catalog for all providers (+regression test). Integrated into release/v3.8.10.
* fix(qoder): bifurcate validation by token type — PAT→Cosy, regular API key→dashscope (#3149)
Bifurcate Qoder validation by token type (PAT→Cosy, regular→dashscope) +regression test. Integrated into release/v3.8.10.
* fix(antigravity): dynamic model resolution via MITM alias table (#3144)
Dynamic antigravity MITM model resolution in the executor (+bug fix +regression test; DB import dropped from client-reachable config). Integrated into release/v3.8.10.
* Feature/batch allow big (#3128)
Podman deployment options + larger upload body-size limits (+CONTAINER_HOST docs). Integrated into release/v3.8.10.
* fix(fireworks): preserve fully-qualified router/model IDs (#3133) (#3160)
Fireworks router IDs (accounts/fireworks/routers/...) were double-prefixed
with accounts/fireworks/models/ → upstream 404. Add optional
acceptedModelIdPrefixes to the registry entry and skip the prepend when the
model already starts with an accepted prefix.
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
* fix(llama-cpp): route to configured local baseUrl instead of OpenAI (#3136) (#3161)
llama-cpp was missing from the local-provider group in buildUrl(), so it
fell through to the OpenAI baseUrl and returned an OpenAI 401. Add the
case to resolve the connection's providerSpecificData.baseUrl.
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
* fix(t3-chat-web): parse cookies + convexSessionId from stored credential (#3007) (#3162)
The executor read credentials.cookies/convexSessionId, but the pipeline
only stores the pasted string under apiKey → t3.chat always 400'd. Parse
both values from apiKey (fallback accessToken), mirroring validation.ts.
Co-authored-by: minhtran162 <minhtran162@users.noreply.github.com>
* fix(minimax): stop capping MiniMax-M3 / M2.7 max_tokens at 8192 (#3141) (#3163)
MiniMax-M3 had no MODEL_SPECS entry and capitalized MiniMax-M2.7 missed
its lowercase spec (case-sensitive lookup) → both fell to the 8192 default
cap. Add the M3 spec (512K output), alias the capitalized ids, and make
getModelSpec lookups case-insensitive.
Co-authored-by: totaltube <totaltube@users.noreply.github.com>
* fix(github-copilot): discover model catalog live from api.githubcopilot.com (#3120, #3121) (#3164)
The github (Copilot) provider had a static hardcoded catalog with no
discovery source, so Import Models never refreshed (#3120) and advertised
non-entitled models that 400 on use (#3121). Add a live /models fetch with
fallback to the static list.
Co-authored-by: gabrielmoreira <gabrielmoreira@users.noreply.github.com>
* fix(combo): invalidate nested-combo cache on edits + log DATA_DIR (#3147) (#3165)
Editing a combo did not invalidate the 10s nested-combo expansion caches
(chat.ts getCombosCachedForChat + chatCore.ts getCombosCached; the exported
clearCombosCache was dead code), so a removed nested target/model could be
served as a phantom for up to 10s. Wire a shared monotonic combos-cache
version in readCache (bumped by invalidateDbCache("combos") on every combo
write); both cache layers treat a version mismatch as a miss.
Also log the resolved DATA_DIR/SQLITE_FILE absolute path at DB init so the
reporter's 'persists across restart + volume wipe' symptom (a multi-replica
Docker volume/DATA_DIR mismatch, not a routing bug) is diagnosable from logs.
Includes consolidated CHANGELOG entries for #3133/#3136/#3007/#3141/#3120/#3121.
Co-authored-by: ViFigueiredo <ViFigueiredo@users.noreply.github.com>
* fix(web-tools): parse bare JSON tool calls (#3157)
Parse bare JSON tool calls for deepseek-web (#2820) + fuzzy tool-name matching. Integrated into release/v3.8.10.
* fix(misc): minor fixes across reasoning cache, account fallback, binary manager (#3177)
Misc: ProviderProfile export, DeepSeek reasoning regex, binary guard. Integrated into release/v3.8.10.
* fix(kiro): minor OAuth social exchange tweaks (#3176)
Kiro social OAuth: optional targetProvider passthrough. Integrated into release/v3.8.10.
* deps: bump hono from 4.12.18 to 4.12.23 (#3179)
Bump hono to 4.12.23. Integrated into release/v3.8.10.
* fix(providerRegistry): update kilocode format and executor (#3166)
kilocode: openai format + default executor (matches kilo-gateway) + registry test. Integrated into release/v3.8.10.
* feat(metrics): cross-request TTFT and gap latency after tool calls (#3173)
Cross-request TTFT + gap-after-tool latency metrics (+test). Integrated into release/v3.8.10.
* feat(dashboard): provider stats API endpoint and dashboard page (#3175)
Provider stats dashboard + API (SQL moved to db module per Hard Rule #5, +test). Integrated into release/v3.8.10.
* fix(usage): sequential+spaced OAuth quota sync, reactive force-refresh, actionable 401 (#3156)
Sequential+spaced OAuth quota sync, reactive force-refresh on 401, actionable 401 in UI. Integrated into release/v3.8.10.
* fix(healthcheck): per-provider proactive-refresh skip list (rescue short-TTL OAuth) (#3159)
Per-provider proactive-refresh skip list (OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS) to rescue short-TTL OAuth. Integrated into release/v3.8.10.
* feat(quota): show OAuth token expiry on provider cards (small, blue, informative) (#3178)
Show OAuth token expiry on provider cards (small, blue, informative). Integrated into release/v3.8.10.
* fix(providers): empty refresh must not resurface just-cleared synced models (#3181)
Empty refresh must not resurface just-cleared synced models (fixes the release-blocking provider-models-route test). Integrated into release/v3.8.10.
* chore(release): v3.8.10 — 2026-06-04 (finalize CHANGELOG)
---------
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Xiangzhe <32761048+xz-dev@users.noreply.github.com>
Co-authored-by: Jan Leon <Jan.gaschler@gmail.com>
Co-authored-by: M.M <mr.maatoug@gmail.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
Co-authored-by: minhtran162 <minhtran162@users.noreply.github.com>
Co-authored-by: totaltube <totaltube@users.noreply.github.com>
Co-authored-by: gabrielmoreira <gabrielmoreira@users.noreply.github.com>
Co-authored-by: ViFigueiredo <ViFigueiredo@users.noreply.github.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>