✨ New Features
- feat(ui): unified Active + Finished requests into a single view — the dashboard now shows in-flight and completed requests in one list with deep-linking, live streaming detail, and a dedicated
/api/logs/[id]detail route; pending requests are tracked per connection and finalized as they complete. (#3401 — thanks @hartmark / @diegosouzapw) - feat(plugins): plugin lifecycle hooks + theme-manager example — adds
onInstall/onActivate/onDeactivate/onUninstalllifecycle events dispatched by the plugin manager, thinsindex.tsto a backward-compatible re-export shim overhooks.ts, and ships theme-manager + request-logger example plugins. (#3473 — thanks @oyi77 / @diegosouzapw) - feat(browserPool): Playwright proxy resolved from the proxy registry — browser-backed providers (claude-web/gemini-web) now route through the configured per-provider/global proxy instead of connecting directly, matching how OAuth/token-refresh already honor
resolveProxyForProvider(closes the VPS IP-rate-limit gap for the browser path). Fully additive with graceful degradation. (#3492 — thanks @borodulin)
🔧 Bug Fixes
- fix(executor): Llama / OpenAI-compat base URL normalization — a
baseURLwithout a path (e.g.llama.example.foo) or with a non-/v1path (e.g.bar.example.com/foo) now correctly gets/v1/chat/completionsappended, fixing the 404 on message sends whileGET /modelstill worked. (#3519 — thanks @hartmark) - fix(sse): empty-choices chunks without usage are dropped instead of injecting retry text — a streamed chunk carrying an empty
choicesarray and nousageis now silently skipped rather than emitting placeholder retry text into the stream, eliminating spurious content for clients that send such keepalive-style frames. (#3513 — thanks @diegosouzapw) - fix(types): restored a clean
typecheck:core— typedgetPendingRequests()to its real shape (Record<string, Record<string, number>>) so the unified-requests view (#3401) no longer treats pending counts asunknown, cast thestreamChunkslog payload to its declared type, and alignedpreScreenTargets(#3169) to the canonicalIsModelAvailablesignature (sync-or-async, normalized viaPromise.resolve). (thanks @diegosouzapw) - fix(opencode-plugin): repaired the corrupted
index.tsthat broke the npmpublish-opencode-pluginbuild (introduced by the #3435 branch) — removed two duplicated code blocks (apiFormat + debug-logging), dropped the localnormaliseFreeLabelsuperseded by thenaming.tsextraction, fixed an undefinedsdkBaseURLreference, declared the missingstartupDebug/logLevelfeature-schema fields, and fixedshortProviderLabeldropping the prefix on a long displayName with no alias. Plugin now builds (DTS clean) with all 254 tests green. (#3435 — thanks @diegosouzapw) - fix(catalog): Codex CLI model-catalog refresh no longer errors —
GET /v1/modelsnow returns a top-levelmodels: []array for Codex clients (detected via theoriginator/user-agent=codex_*headers it sends onGET /v1/models?client_version=...), socodex_models_managerstops failing to decode the OpenAI-standard response and no longer logsfailed to refresh available modelson every startup. The array is intentionally empty: Codex replaces its built-in per-model agent prompt (base_instructions, ~21k chars) with whatever a populated entry carries for the selected model, so emitting our catalog would break Codex's agent behaviour — an empty list keeps Codex on its built-in model info (same inference as before, minus the error). Non-Codex OpenAI clients receive the unchanged{object,data}response. (#3481 — thanks @diegosouzapw) - fix(provider): Cursor's Responses-API-shaped bodies on
/chat/completionsare detected and handled — a body withinputbut nomessagesis now classified asopenai-responses(instead of forcingopenaiand building from undefinedmessages→ upstream 400); standard OpenAI clients are unaffected by themessages===undefinedguard. (#3490 — thanks @borodulin) - fix(sse): numeric provider IDs normalized to strings across 4 more surfaces — extends #3427 to the Responses-API SSE passthrough (
response_id/item_id/call_id), the buffered/flush path instream.ts, the dedup-key builders, andsseParser.ts, preventingundefinedlookups when IDs arrive as numbers. (#3451 — thanks @disafronov) - fix(theoldllm):
X-Request-Tokengenerated server-side, dropping the Playwright dependency — replicates the site's clientrie()token (djb2 hash +oldllm-client-2026seed + UA prefix + 8-hexcrypto.randomUUIDsuffix) directly, so The Old LLM no longer needs a headless browser to mint tokens. (#3491 — thanks @borodulin / @diegosouzapw) - fix(combo): parallel pre-screen + circuit-breaker fast-exit for priority combos — provider profiles and model availability for all targets are pre-screened concurrently (max 5), and targets whose circuit breaker is OPEN are skipped immediately, reducing first-token latency on multi-target priority combos. (#3169 — thanks @pizzav-xyz)
- fix(authz): URL-tokenized client endpoints (
/api/v1/vscode/<key>/...) authenticate again when the caller sends its own non-OmniRouteAuthorizationheader — a non-Bearer <token>header (e.g. VS Code Copilot's own, or an emptyBearer) no longer short-circuits auth; it falls through to the path-scoped URL token (still validated downstream), instead of 401'ing underREQUIRE_API_KEY=true. (#3504 — thanks @zhiru / @diegosouzapw) - fix(playground): the dashboard provider Test playground works under
REQUIRE_API_KEY=true— it previously sent the masked key (sk-xxxx****yyyy) as a bearer (always invalid → 401). It now authenticates via the dashboard session and sends only the key id (x-omniroute-playground-key-id); the gateway resolves the secret server-side, honored only for an authenticated session and never putting the key secret on the wire. (#3503 — thanks @zhiru / @diegosouzapw)
📝 Maintenance
- feat(docs): doc-accuracy gate — new
npm run check:fabricated-docs(scripts/check/check-fabricated-docs.mjs) indexes the codebase (api routes, env vars, CLI commands) and flags API-path/env-var/CLI/hook/file-ref claims indocs/**+AGENTS.mdthat don't exist in source (soft-fail by default,--strictfor CI; wired intocheck:docs-all). Also refreshes the AGENTS.md live counts against source. (#3510 — thanks @oyi77) - chore: ignore local quality reports and prompt artifacts (
quality-metrics.json,PLANO-/RELATORIO-QUALITY-GATES.md, stray prompt.txtfiles) so they no longer surface ingit status. (thanks @diegosouzapw)
🔒 Security
- fix(opencode-plugin): bounded the regex quantifiers in
normaliseFreeLabelto close a polynomial-ReDoS (CodeQLjs/polynomial-redos) — an unbounded\s*before an anchored\s*$allowed O(n²) backtracking on attacker-influenced provider/model display names; bounded to{0,8}/{1,8}. (thanks @diegosouzapw)
What's Changed
- fix(providerRegistry): update Claude model entries to latest versions by @androw in #3521
- Release v3.8.18 by @diegosouzapw in #3482
Full Changelog: v3.8.17...v3.8.18