Files
OmniRoute/docs/i18n/zh-CN/README.md
Diego Rodrigues de Sa e Souza 1bda6c15dc Release v3.8.44 (#5925)
* fix(install): add pnpm-workspace.yaml allowBuilds + pnpm.json for pnpm 11+

pnpm 11 introduced ERR_PNPM_IGNORED_BUILDS for native addon packages.
Without explicit allowBuilds approval, these packages silently skip build scripts
and OmniRoute fails to start with missing native modules.

Changes:
- pnpm-workspace.yaml: Set allowBuilds=true for all 13 native addon packages
  (@parcel/watcher, @swc/core, better-sqlite3, core-js, esbuild, keytar, koffi,
  libxmljs2, onnxruntime-node, protobufjs, sharp, tls-client-node, unrs-resolver)
- pnpm.json: Migrate onlyBuiltDependencies from package.json (deprecated field)
  to the new pnpm.json config file per pnpm 11 spec.

Tested on: pnpm 11.9.0, Node 24, Windows 11.

Fixes: pnpm install ERR_PNPM_IGNORED_BUILDS on fresh clone with pnpm 11.

* chore(release): open v3.8.44 development cycle

* test(security): parse Kimi Web URL host instead of substring match (CodeQL #689) (#5928)

Alert js/incomplete-url-substring-sanitization: the Kimi Web executor
test asserted result.url.includes("www.kimi.com"), which a hostile host
like www.kimi.com.evil.net would also satisfy. Parse the URL and assert
on the exact hostname (new URL(result.url).hostname === "www.kimi.com"),
which is both a stronger check and clears the CodeQL warning.

* refactor(translator): extract thinking-budget fitting from openai-to-claude (#5932)

Extract the thinking-budget fitting cluster (fitThinkingToMaxTokens +
private safeCapMaxOutputTokens + MIN_* constants) verbatim into the pure
leaf openai-to-claude/thinkingBudget.ts. Host re-exports fitThinkingToMaxTokens
so external importers keep working and imports it back for internal use.

Host 822 -> 738 LOC (under the 800 cap). No behavior change: byte-identical
bodies, public export set unchanged. Adds a split-guard test; all consumer
tests stay green (translator-openai-to-claude, strip-empty, minimax-m3, passthrough).

* chore(release): pipeline hardening — test-masking pre-flight gate + contributors/uncovered helpers (#5926)

* chore(ci): add test-masking PR-context gate to release-green pre-flight

Reproduce check:test-masking (vs origin/main) inside validate-release-green so
non-allowlisted net-assert reductions surface in the local pre-flight instead of
in a ~40-min CI layer on the release PR. run() now merges a per-gate opts.env so
GITHUB_BASE_REF reaches the child. HARD gate; skipped under --quick.

Context: v3.8.43 release cost 3 CI round-trips for PR-context gates (test-masking,
file-size, pr-evidence) that check:release-green did not reproduce locally.

* chore(release): add contributors generator + uncovered-commit reconciliation helpers

- scripts/release/gen-contributors.mjs: reproducible `### 🙌 Contributors` table for a
  CHANGELOG version (parenthetical-group parser → accurate per-PR attribution, noise-handle
  denylist). v3.8.43 shipped without the section (a real miss) because it was hand-built.
  npm run release:contributors <version> [--inject].
- scripts/release/list-uncovered-commits.mjs: lists commits since the last tag with no
  CHANGELOG bullet (v3.8.43 had 123/176 uncovered at reconciliation start). Advisory,
  maintainer-side. npm run release:uncovered.
- 20 unit tests (parenthetical attribution, noise exclusion, idempotent injection, coverage window).

* chore(quality): absorb web-cookie-providers-new file-size drift from #5928 (base-red on release/v3.8.44)

* refactor(translator): split openai-responses request translator into pure leaves (#5940)

Extract the shared pure primitives and the chat->Responses direction out of the
894-line openai-responses.ts request translator:
- openai-responses/helpers.ts: pure primitives (toRecord/toString/clampCallId/
  normalizeVerbosity/etc + markers/regexes/JsonRecord), zero host imports
- openai-responses/toResponses.ts: openaiToOpenAIResponsesRequest (chat->Responses),
  imports the helpers leaf

Host keeps openaiResponsesToOpenAIRequest (Responses->chat, imported by production)
plus both register() directions, and re-exports openaiToOpenAIResponsesRequest so
external importers (tests) keep working.

Host 894 -> 529 LOC (under the 800 cap). Verbatim bodies (multiset check: leaf A 54/54,
leaf B 294 lines, fn1 intact), public export set unchanged, leaves never import the host
(no cycle). Adds a split-guard test; all consumer tests stay green (responses-translation-fixes
37, verbosity 4, reasoning-effort 4, orphaned-tool-filter 8, empty-tool-name-loop 8,
headroom-responses-format 3).

* chore(ci): pr-evidence FAIL output tells you to push (body edit does not re-run the gate) (#5944)

ci.yml ignores the 'edited' event, so adding the Evidence block to the PR body after a
push does not re-run check:pr-evidence — you need another commit. The FAIL report now
says so, at the exact place someone sees the red check. + 5 unit tests (classification +
hint-on-fail / no-hint-on-pass). Decided against a separate edited-triggered workflow:
pr-evidence is not a required check (no ruleset gates it; release PRs merge UNSTABLE, not
BLOCKED), so the gap is cosmetic and the generate-release skill already puts Evidence in
the body before the first push.

* fix(providers): Perplexity Web emits real tool_calls in streaming mode (mirror chatgpt-web toolMode) (#5927) (#5937)

Perplexity Web (Pro/Max) only converted <tool>{...}</tool> text into
OpenAI tool_calls for non-streaming requests (hasTools && !stream).
Streaming requests -- the default for agentic coding clients -- got
the raw <tool> text as plain delta.content and never emitted a
tool_calls SSE delta, so clients could not execute tools.

Reuses the provider-agnostic buildToolModeResponse()/
toolCompletionToSseStream() helpers already shipped for chatgpt-web
(#5240): when tools are requested, buffer the full completion and
convert it into either a JSON completion or a terminal SSE replay
carrying delta.tool_calls + finish_reason: tool_calls, regardless of
the caller's stream flag. Extended buildToolModeResponse()'s idSeed
to be caller-supplied (default 'cgpt', perplexity-web passes 'pplx')
so tool_call ids stay provider-specific without duplicating the
helper. Non-tool streaming is unchanged (still lives token-by-token
via buildStreamingResponse).

* fix(discovery): resolve duplicate /v1 paths and redirect aborts (#5904)

Integrated into release/v3.8.44. Thanks @hamsa0x7 for diagnosing the doubled /v1 discovery path and the REDIRECT_BLOCKED probe-loop abort (#5899). De-scoped to the discovery fix (the #5903 session-affinity work is handled by #5943) and added Rule #18 regression guards.

* docs(changelog): record #5926 + #5944 (release-pipeline hardening) under v3.8.44 Maintenance (#5952)

* docs(claude): add Hard Rule #22 — cross-session safety (git stash + in-flight PRs) (#5955)

Integrated into release/v3.8.44 — Hard Rule #22 (cross-session safety).

* refactor(translator): extract pure helpers from response/openai-responses (#5949)

Extract the 5 stateless helpers (normalizeToolName, stripEmptyOptionalToolArgs,
normalizeOutputIndex, normalizeUpstreamFailure, extractResponsesReasoningSummaryText)
verbatim into the pure leaf openai-responses/pureHelpers.ts (no stream state, no host
import). Host imports them back and re-exports normalizeUpstreamFailure for external
importers (tests).

Host 1091 -> 1001 LOC. The stateful streaming core stays in the host (out of scope).
Byte-identical bodies (multiset 73/73), no cycle. Adds a split-guard; consumer tests
stay green (responses-translation-fixes 37, combo-param-validation-fallback-4519 5).

* docs(compression): document upstream sync policy for RTK/Caveman engines (#5830) (#5948)

Integrated into release/v3.8.44 — docs-only upstream sync policy for RTK/Caveman engines (closes #5830). All 7 checks green.

* fix(sse): strip ANSI/VT100 codes from gemini-cli stream frames (#5934)

Integrated into release/v3.8.44 — ReDoS-safe ANSI/VT100 strip for gemini-cli stream frames (port of upstream #2273, thanks @anki1kr). PR test green (5/5), file-size gate OK.

* fix(translator): strict Anthropic content-block compliance in antigravity→openai request (#5935)

Integrated into release/v3.8.44 — strict Anthropic content-block compliance in antigravity→openai (port upstream #2296). PR test green (9/9). UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path), not a regression from this PR.

* fix(mcp): auto-recover stale streamable HTTP sessions on initialize (#5957)

Integrated into release/v3.8.44 — MCP stale streamable-HTTP session auto-recovery (thanks @Chewji9875).

* fix(providers): validate v0 Platform API keys via chats endpoint (#5954)

Integrated into release/v3.8.44 — v0-vercel Platform API key validation (thanks @vittoroliveira-dev).

* fix(api): relax provider-scoped chat completion validation (#5907)

Integrated into release/v3.8.44 — relaxed provider-scoped chat validation + regression test (thanks @nickwizard).

* fix(providers): strip /v1 unconditionally to avoid /v1/v1/models fetch error (#5899) (#5920)

Integrated into release/v3.8.44 — unconditional /v1 strip in both models-discovery paths + regression test (thanks @anki1kr).

* fix(resilience): per-window is_exhausted + honor quota-exhaustion preflight for priority combos (#5923) (#5941)

Integrated into release/v3.8.44.

* fix(resilience): honor active codex session affinity over per-request reset-aware re-scoring (#5903) (#5943)

Integrated into release/v3.8.44.

* fix(thinking): only inject redacted_thinking replay block when tool_use present and thinking enabled (#5945) (#5953)

Integrated into release/v3.8.44.

* feat(providers): add ClinePass API-key provider (#5942)

Integrated into release/v3.8.44 — ClinePass API-key (BYOK) provider (port upstream 9router#2304, co-authored @adentdk). Validated locally: 16 clinepass tests green; fixed the APIKEY count 158→159 + translate-path golden snapshot (clinepass is a genuine new provider). Remaining UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path). Supersedes stub #5541.

* feat(api): add /v1/ocr endpoint (Mistral OCR) + Mistral moderation (#5950)

Integrated into release/v3.8.44 — /v1/ocr endpoint (Mistral OCR) + Mistral moderation (port upstream 9router#2064, co-authored @waguriagentic). Validated locally: 14 ocr-route tests + moderation/servicekind/endpoint-category suites green (CORS→Zod→handler + no-stack-leak assertion). Reds are inherited DRIFT only: cognitive-complexity ratchet (none from OCR files — pre-existing cycle drift, rebaselined at release) + environmental setup-claude base-red.

* fix(codex): convert chat json schema to responses text format (#5933)

Integrated into release/v3.8.44 — converts Chat Completions json_schema response_format → Responses API text.format on the Codex path, and preserves existing text.format through verbosity normalization. Base redirected main→release; the openai-responses.ts split that landed this cycle was reconciled by re-applying the delta onto openai-responses/toResponses.ts. Validated locally: 48 translator-openai-responses-req + 8 codex-verbosity tests green.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(providers): add Claude Sonnet 5 support across the model pipeline (#5833)

Integrated into release/v3.8.44 — wires claude-sonnet-5 end-to-end (registries, modelSpecs, pricing ×3, cost, Sonnet-family fallback, 1M-ctx, static models). Reconciled the add/add overlap with the already-merged #5796 (kept the PR's superset test with the family-fallback assertion). Validated locally: kiro-sonnet-5 + catalog + pricing/modelSpecs/fallback suites all green. Thanks @ggiak!

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(relay): gate bifrost auto routing by provider manifest (#5870)

Integrated into release/v3.8.44 — gates Bifrost auto-routing by the provider plugin manifest (only manifest-eligible providers reach the sidecar; ineligible/unknown fall back to the TS path with explicit reasons). Superset of #5869 (carries the full manifest + registry + docs). Resolved an integration-test conflict in favor of the release (which already subsumes this PR's readiness/removeDirWithRetry improvements). Validated locally: 4 provider-plugin-manifest + 11 relay-routing-backend tests green. Thanks @KooshaPari!

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* refactor(translator): extract pure message helpers from openai-to-kiro (852→751) (#5947)

* refactor(translator): extract pure message helpers from openai-to-kiro

Extract the pure tool/message helpers (parseToolInput, normalizeKiroToolSchema,
serializeToolResultContent) verbatim into the leaf openai-to-kiro/messageHelpers.ts.
The host imports them back for convertMessages. They were module-private, so the
public export set is unchanged (no re-export needed).

Host 852 -> 751 LOC. Byte-identical bodies (multiset 99/99), leaf has zero imports
(no cycle). Adds a split-guard; consumer tests stay green (translator-openai-to-kiro 33,
translator-ai-sdk-image-parts 3).

* chore: re-trigger CI (stuck runner on 2/2 shard)

* refactor(executors): extract pure prompt + composer helpers from cursor (#5960)

Extract two pure clusters from the cursor executor into sibling leaves:
- cursor/prompt.ts: isRecordLike + toolChoiceDirectiveLine + buildCursorOutputConstraints
- cursor/composer.ts: composer thinking-as-content decoding (isComposerModel,
  visibleComposerContentFromThinking, composerReasoningRemainder + markers)

Host imports both back for internal use and re-exports the 3 composer helpers for
external importers (tests). Host 1576 -> 1451 LOC. Byte-identical bodies (verbatim
multiset prompt 65/65, composer 32/32), leaves have zero imports (no cycle). Adds a
split-guard; consumer tests stay green (cursor-composer-thinking, cursor-streaming,
cursor-agent-tool-calls, translator-openai-to-cursor, cursor-agent-system-prompt).

* refactor(executors): extract pure SSE-collect parsing from antigravity (#5962)

Extract the pure SSE-payload -> collected-stream parser (AntigravityCollectedStream,
stripZeroWidth, parseAntigravityTextualToolCall, addAntigravityTextualToolCall,
processAntigravitySSEPayload/Text, flushAntigravitySSEText) verbatim into the leaf
antigravity/sseCollect.ts. Host imports the helpers it uses and re-exports
processAntigravitySSEPayload for external importers (tests).

Host 1812 -> 1671 LOC. Byte-identical bodies (verbatim multiset 135/135), leaf does
not import the host (no cycle). Credit/quota state, auth, and HTTP dispatch untouched.
Adds a split-guard; consumer tests stay green (executor-agy 8, executor-antigravity 26,
antigravity-sse-collect-socket-release, copilot-agent-antigravity-parity 6).

* refactor(executors): extract pure model maps + resolvers from chatgpt-web (#5967)

Extract the static model maps (MODEL_MAP, MODEL_FORCED_EFFORT, THINKING_CAPABLE_SLUGS)
and the pure thinking-effort resolvers (isThinkingCapableModel, normalizeThinkingEffort,
resolveThinkingEffort, ResolvedChatGptModel, resolveChatGptModel) verbatim into the pure
leaf chatgpt-web/models.ts. Host imports the two resolvers it uses back.

Host 3205 -> 3076 LOC. Byte-identical bodies (verbatim multiset 120/120), leaf has zero
imports (no cycle). Auth/PoW/session/HTTP dispatch and all module caches untouched.
Adds a split-guard; consumer tests stay green (chatgpt-web 86, chatgpt-web-tools-5240 4,
chatgpt-web-sha3-boringssl-5531 5).

* refactor(executors): decompose grok-web into pure tool/markup leaves (#5994)

Extract the pure OpenAI<->Grok tool-translation, native-tool mapping, markup cleanup,
and NDJSON stream types out of the 1872-line grok-web executor into 4 sibling leaves:
- grok-web/types.ts: GrokStreamResponse/GrokStreamEvent (stream types)
- grok-web/tool-bridge.ts: OpenAI<->Grok tool translation + registry + classifiers
- grok-web/native-tools.ts: native-tool selection/scoring + native->OpenAI mapping
- grok-web/text-cleanup.ts: Grok markup stripping + GrokMarkupFilter

Layered, acyclic: types <- tool-bridge <- native-tools; text-cleanup <- types; host
imports the leaves. All symbols module-private (no host re-export). Host 1872 -> 887 LOC.
Byte-identical bodies (verbatim per-leaf), no cycle, all new leaves <= 800 cap
(tool-bridge split at line 753 to stay under). Auth/cookie/TLS/HTTP dispatch untouched.
Adds a split-guard; consumer tests stay green (grok-web 62, grok-cli-oauth 15,
grok-cli-strip-params 2).

* refactor(executors): extract pure quota parsing from codex (#5999)

Extract the pure Codex quota-snapshot parsing + reset/cooldown scheduling
(CodexQuotaSnapshot, parseCodexQuotaHeaders, getCodexResetTime,
getCodexDualWindowCooldownMs) verbatim into the leaf codex/quota.ts. Host re-exports
the 4 symbols so handlers/chatCore/codexQuota.ts + tests keep resolving.

Host 1539 -> 1427 LOC. Byte-identical bodies (verbatim 98/98), leaf has zero imports
(only Date, no cycle). WS transport, auth, HTTP dispatch untouched. Adds a split-guard;
consumer tests stay green (executor-codex 40, codex-quota-fetcher 7, chatcore-codex-quota 5).

* refactor(executors): extract pure stream formatters from deepseek-web (#6000)

Extract the pure content/citation formatters (isThinkingModel, isSearchModel,
cleanDeepSeekToken, formatStreamContent, DeepSeekSearchResult, appendSearchCitations)
verbatim into the leaf deepseek-web/stream-format.ts. Host imports the 5 it uses back
into transformSSE/collectSSEContent (cleanDeepSeekToken stays internal to the leaf).

Host 1147 -> 1108 LOC. Byte-identical bodies (verbatim 34/34), leaf has zero imports
(no cycle), all module-private (no re-export). PoW/auth/token-cache/HTTP dispatch
untouched. Adds a split-guard; consumer tests stay green (deepseek-web 35,
deepseek-web-rolling-window-2942 5, deepseek-web-tools-execute 3).

* refactor(api): add validatedJsonBody helper (salvage #5075) (#5931)

Fuses JSON body parsing + Zod validation into a single call that returns
either type-narrowed data or a ready-to-return 400 NextResponse with the
standard error envelope. Salvaged as the Tier 1 portable helper from the
closed refactor PR #5075; the bulk route migration is intentionally not
ported. Adds a focused 6-case regression test.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* feat(qoder): drive PAT auth via qodercli, add dashboard quota, fix connection display (#5816)

Integrated into release/v3.8.44 — Qoder PAT auth via qodercli binary + dashboard quota + dual-auth connection fix. Thanks @AgentKiller45 (co-author @judy459)!

Validated locally (release-green on its own merits): lint 0, typecheck:core 0, 104 qoder/usage/UI tests green, file-size gate OK (owner-approved qoderCli.ts baseline-freeze 666→989), env-doc-sync fixed (documented QODER_CLI_CONFIG_DIR).

The 2 remaining CI reds are INHERITED base-reds, not caused by this PR: (1) LEDGER-4 minimax-m3 supportsVision (minimax-m3 base + cline-pass/minimax-m3 from the already-merged #5942); (2) mutation-test-coverage missing 3 tests in stryker.conf (#5903/#5942/#5923). Both cleaned up separately.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(providers): minimax-m3 supportsVision (LEDGER-4) + stryker tap.testFiles drift (#6012)

Release-green cleanup — clears LEDGER-4 minimax-m3 supportsVision + stryker tap.testFiles drift base-reds. Validated locally.

* fix(registry): flag cline-pass/minimax-m3 as multimodal (supportsVision) (#6003)

The cline-pass provider's minimax-m3 entry was missing supportsVision, breaking the
LEDGER-4 registry-consistency test (all minimax-m3 entries must set supportsVision to
match lite.ts — minimax-m3 is multimodal). Every other minimax-m3 registry entry
(trae, bazaarlink, cline, ollama-cloud, ...) already sets it. This was a base-red on
release/v3.8.44 inherited by every open PR.

Validated by the existing failing-then-passing guard tests/unit/review-reviews-v3814-fixes.test.ts
(LEDGER-4).

* refactor(executors): extract pure payload construction from claude-web (#6006)

Extract the pure Claude-web payload types + transforms + default tools/style
(ClaudeWebRequestPayload, ClaudeWebStreamChunk, DEFAULT_CLAUDE_MODEL,
generateMessageUUIDs, getDefaultTools, getDefaultPersonalizedStyle, transformToClaude,
transformFromClaude) verbatim into the leaf claude-web/payload.ts. Host imports the 3
it uses back (ClaudeWebRequestPayload type + the two transforms).

Host 1056 -> 835 LOC. Byte-identical bodies (verbatim 149/149), leaf imports only
randomUUID (no host import, no cycle), all module-private (no re-export). Cookie/auth/
Turnstile/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green
(claude-web 13, claude-web-auto-refresh 6).

* refactor(executors): extract pure upstream-header helpers from base (#6008)

Extract the pure upstream-header helpers (mergeUpstreamExtraHeaders, getCustomUserAgent,
setUserAgentHeader, applyConfiguredUserAgent, isOpenAICompatibleEndpoint,
stripStainlessHeadersForOpenAICompat) verbatim into the leaf base/headers.ts. base.ts is
imported by ~18 executors, so the host re-exports all 6 to keep those import paths intact;
it also imports the 4 it uses internally in the BaseExecutor class. The trivial JsonRecord
type alias is redefined locally in the leaf to avoid a base<->leaf cycle.

Host 1539 -> 1451 LOC. Byte-identical bodies (verbatim 78/78), leaf does not import the
host (no cycle). typecheck:core validates all base importers still resolve via the
re-export. Adds a split-guard; consumer tests stay green (executor-base-utils 22,
executor-default-base 49, executor-strip-stainless-openai-compat 6, plus executor sanity
via typecheck).

* refactor(executors): extract pure wire protocol from perplexity-web (#6014)

Extract the pure Perplexity wire protocol (consts, SSE stream types, SSE parsing,
OpenAI<->Perplexity message translation, request/query builders, content extraction,
sseChunk) verbatim into the leaf perplexity-web/protocol.ts. Host imports back the 10
symbols it uses; everything module-private (no re-export). Session cache, TLS fetch,
auth, and the executor class stay in the host.

Host 1028 -> 534 LOC. Byte-identical bodies (verbatim), leaf imports only randomUUID
(no host import, no cycle). Adds a split-guard; consumer tests stay green
(perplexity-web 26, streaming-tools-5927 2, tls-client 6, key-validation-models 2).

* refactor(executors): extract pure URL normalizers from default (#6015)

Extract the pure per-provider chat-URL normalizers (normalizeBailianMessagesUrl,
normalizeDataRobotChatUrl, normalizeAzureAiChatUrl, normalizeWatsonxChatUrl,
normalizeOciChatUrl, normalizeSapChatUrl, normalizeXiaomiMimoChatUrl,
normalizeOpenAIChatUrl, getOpenRouterConnectionPreset) verbatim into the leaf
default/urlNormalizers.ts. Host imports them back into buildUrl/transformRequest; the
now-dead build*ChatUrl/normalizeBaseUrl imports move to the leaf. All module-private
(no re-export).

Host 864 -> 815 LOC (shrunk below its frozen baseline). Byte-identical bodies (verbatim
45/45), leaf does not import the host (no cycle). buildHeaders/execute/auth untouched.
Adds a split-guard; consumer tests stay green (executor-default-base 49,
anthropic-compatible-bearer 3, strip-client-metadata 3).

* feat(webfetch): support self-hosted FireCrawl instances (#5793)

Integrated into release/v3.8.44 — self-hosted FireCrawl support (FIRECRAWL_BASE_URL/FIRECRAWL_TIMEOUT_MS). Re-cut clean onto the release tip (branch was fossilized from a pre-v3.8.40 snapshot). Validated: 4 firecrawl tests green, env-doc-sync + docs-sync pass. UNSTABLE red is the inherited environmental setup-claude base-red.

* feat(xai): register XaiExecutor with reasoning-effort suffix parsing (#5800)

Integrated into release/v3.8.44 — XaiExecutor with reasoning-effort suffix parsing. Re-cut clean onto the release tip (branch was fossilized). Validated: 6 xai-executor tests green, provider-consistency OK, typecheck:core 0 errors, env-doc-sync in sync. UNSTABLE red is the inherited environmental setup-claude base-red.

* feat(discovery): Phase 2 — reporter, /api/discovery/* routes (strict loopback-only) + dashboard UI (#5939)

* feat(discovery): Phase 2 reporter — discoveryResults DB module + service wiring

Adds src/lib/db/discoveryResults.ts (CRUD over the discovery_results table
from migration 074) and wires the opt-in discovery service to persist and read
findings through it: persistDiscoveryResult / getDiscoveryResults /
getDiscoveryResultById / markVerified / deleteDiscoveryResult, with
(provider, method, endpoint) upsert de-duplication. Re-exported from localDb.

The service stays opt-in / default-off. The /api/discovery/* routes and the
dashboard UI tab are intentionally deferred to Phase 2b — they need the
local-only enforcement model (Hard Rules #15/#17 territory) decided first.

TDD: tests/unit/db/discovery-results.test.ts (8 cases, DB + service delegation),
isolated DATA_DIR with resetDbInstance cleanup.

* feat(discovery): Phase 2b — /api/discovery/* routes (strict loopback-only)

Adds the discovery HTTP surface on top of the reporter DB module:
  GET    /api/discovery/results            list findings (optional ?providerId)
  GET    /api/discovery/results/:id        one finding (404 if absent)
  DELETE /api/discovery/results/:id        delete a finding
  POST   /api/discovery/scan               scan a provider + persist findings
  POST   /api/discovery/verify/:id         mark a finding verified

Authorization: strict loopback-only. "/api/discovery/" is added to
LOCAL_ONLY_API_PREFIXES so the central authz pipeline (proxy.ts →
runAuthzPipeline → managementPolicy) rejects non-loopback callers with a 403
LOCAL_ONLY before any handler runs. It is deliberately NOT in
LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES — no remote manage-scope bypass —
because POST /scan issues outbound probes to provider endpoints (SSRF-adjacent)
and must never be tunnel-reachable. Handlers also call requireManagementAuth
(defense in depth) and return sanitized errors via createErrorResponse.

Tests:
- tests/unit/authz/discovery-routes-local-only.test.ts (8) — security guard:
  isLocalOnlyPath true + not manage-scope-bypassable for all four paths.
- tests/unit/api/discovery-routes.test.ts (6) — handler integration over an
  isolated DATA_DIR: list/filter, by-id 200/404/400, scan persist + 400 on
  empty/malformed body, verify 200/404, delete 200/404, no stack-trace leak.

* feat(discovery): Phase 2c — dashboard UI tab (Tools → Discovery)

Adds the /dashboard/discovery page (DiscoveryPageClient) that consumes the
Phase 2b /api/discovery/* routes: scan a provider, list findings, verify or
delete them. Registered in the sidebar under the Tools group (icon
travel_explore) and given a "discovery" i18n namespace + sidebar keys in
en.json (other locales fall back to en via next-intl until synced — the
locale files are in a pre-existing coverage deficit unrelated to this change).

Registers the UI test path in vitest.config.ts (advisory ui suite).

Tests: src/app/(dashboard)/dashboard/discovery/__tests__/DiscoveryPageClient.test.tsx
(3 cases: loads+renders results, empty state, fetches /api/discovery/results on
mount; stable useTranslations mock to avoid the fetch-loop). NOTE: the ui vitest
suite cannot run in this workspace — @testing-library/dom (a @testing-library/
react peer dep) is absent from node_modules, which fails ALL existing ui tests
equally; the test runs in CI. Component verified locally via typecheck + lint.

* test(discovery): register discovery-routes-local-only in stryker tap.testFiles

The mutation-test-coverage gate (--strict) flags any unit test covering a
mutated module that isn't listed in stryker.conf.json tap.testFiles. This PR's
tests/unit/authz/discovery-routes-local-only.test.ts covers src/server/authz/
routeGuard.ts (a mutated module, which this PR edits by adding the
/api/discovery/ local-only prefix), so it must be registered for its mutant
kills to count. No behavior change.

* refactor(discovery): split DiscoveryPageClient to satisfy max-lines-per-function

The complexity ratchet (max-lines-per-function: 80) flagged the single
184-line DiscoveryPageClient function (+1 over baseline). Extract the data
layer into two hooks (useDiscoveryResults for list/loading/feedback,
useDiscoveryActions for scan/verify/delete), a shared callApi helper, and two
presentational sub-components (DiscoveryScanForm, DiscoveryResultCard). Every
function is now under the 80-line ceiling; complexity gate back to baseline
1995. No behavior change — same exported component, same endpoints, same props.

* test(sidebar): include discovery in omni-proxy item-order snapshot

Adding the Discovery item to the Tools group (this PR's sidebar entry) extends
the ordered omni-proxy section list. Update the exact-match deepEqual snapshot
in sidebar-visibility.test.ts to include "discovery" in its position (after
traffic-inspector). The assertion stays exact — this reflects the intentional
new item, it does not weaken the check.

* docs(changelog): restore release bullets eaten by merge auto-resolve; re-add discovery bullet additively

* chore(quality): bump testFrozen for translator-openai-responses-req.test.ts (1097 -> 1172)

Base-red inherited from #5933, which grew the test file to 1171 lines
(Hard Rule #18 regression tests) without adjusting the frozen cap. The
release tip itself fails check:file-size; this unblocks every PR into
release/v3.8.44. File untouched by this PR.

* chore(quality): restore stryker tap.testFiles entries eaten by merge auto-resolve

The merge of origin/release/v3.8.44 silently dropped the 3 entries added
on the release side (#5903, clinepass, #5923). Took the release version
verbatim and re-added only this PR's entry (discovery-routes-local-only)
in alphabetical order. check:mutation-test-coverage green locally.

* chore(quality): reconcile inherited v3.8.44 merge-burst drift + include discovery in tools-group order test

- complexity 1995->2003 and cognitive 856->859: both measure IDENTICAL on
  the pristine release tip (3a3d618fe) and this PR's merged HEAD — the PR
  is complexity-net-zero; drift is from the 2026-07-02 merge burst
  (notes added to both baselines, same family as prior reconciliations).
- sidebar-tools-group.test.ts: append 'discovery' to the expected
  TOOLS_GROUP order — the intentional new sidebar item this PR adds
  (same expected-value update already made in sidebar-visibility.test.ts).

* feat(providers): custom icon URL for compatible provider nodes (#5815)

Integrated into release/v3.8.44 — custom icon URL for compatible provider nodes (DB migration 113 + nodes.ts + Zod schema + API routes + catalog + ProviderIcon UI). Re-cut onto the release tip (branch was fossilized ~13 real files); reconciled icon_url into the release's evolved nodes.ts/routes via 3-way. Validated: 14 backend + 5 frontend(vitest) + 24 page-utils tests green, typecheck:core 0, provider-consistency OK, file-size/env-doc-sync pass. UNSTABLE red is the inherited environmental setup-claude base-red.

* feat(api): add /v1/audio/translations endpoint (#5809)

Integrated into release/v3.8.44 — /v1/audio/translations endpoint (Whisper-style audio translation) + audioTranslation handler + translation providers in audioRegistry. Re-cut clean onto the release tip (branch was fossilized). Validated: 8 route tests (incl. no-stack-leak), typecheck:core 0, route-guard-membership OK, docs gates pass. UNSTABLE red is the inherited environmental setup-claude base-red.

* feat(dashboard): wildcard-CORS runtime warning + CORS security doc (#5602) (#5759)

Integrated into release/v3.8.44 — wildcard-CORS runtime warning banner + docs/security/CORS.md security guide (#5602). Re-cut clean onto the release tip (branch was fossilized). Validated: 20+9 backend + 2 banner(vitest) tests green, typecheck:core 0, docs-sync/symbols/fabricated/doc-links pass. UNSTABLE red is the inherited environmental setup-claude base-red.

* refactor(executors): extract pure JSONL stream translation from huggingchat (#6016)

Extract the pure JSONL->OpenAI-SSE translation (sseChunk, parseJsonlLine,
streamJsonlToOpenAi, readJsonlResponse) verbatim into the leaf huggingchat/jsonlStream.ts.
They consume a passed-in ReadableStream (no fetch/network/state). Host imports back the
two it uses; all module-private (no re-export).

Host 812 -> 594 LOC. Byte-identical bodies (verbatim), leaf has zero imports (no cycle).
Cookie/auth/multipart/execute untouched. Adds a split-guard; consumer tests stay green
(executor-huggingchat 6, huggingchat-model-catalog 3).

* refactor(executors): extract pure Meta AI response parser from muse-spark-web (#6017)

Extract the pure Meta AI SSE/JSON response parsing + content/reasoning/error extraction
(parseMetaSseFrames, readMetaJsonPayloads, collect*/extract*/classify* helpers,
parseMetaAiResponseText, isRecord, the reasoning/renderer key arrays, MetaSseFrame/
ParsedMetaAiResponse types) verbatim into the leaf muse-spark-web/response-parser.ts.
Host imports back the 3 it uses; all module-private (no re-export).

Host 1301 -> 925 LOC. Byte-identical bodies (verbatim), leaf has zero imports (no cycle).
Conversation cache, cookie/auth, fetch, executor class untouched. Adds a split-guard;
consumer tests stay green (muse-spark-cookie-copy-5449 2, muse-spark-web-continuation 6).

* refactor(executors): extract pure EventStream framing from kiro (#6018)

Extract the pure AWS EventStream binary framing (ByteQueue, CRC32 table + crc32,
TEXT_ENCODER/TEXT_DECODER, KIRO_VERIFY_FULL_CRC, parseEventFrame, EventFrame type)
verbatim into the self-contained leaf kiro/eventstream.ts (local JsonRecord alias to avoid
a cycle). Host imports back the 3 it uses (ByteQueue, TEXT_ENCODER, parseEventFrame).

Host 943 -> 758 LOC. Byte-identical bodies (verbatim 145/145), leaf has zero host imports
(no cycle). Auth/token-refresh/streaming-state/executor class untouched; the test-imported
flushBufferedToolArgs/resolveKiroRegion/kiroRuntimeHost stay exported on the host. Adds a
split-guard; consumer tests stay green (executor-kiro 9, kiro-tool-args-streaming 7,
kiro-iam-region 10).

* refactor(executors): extract challenge solver from duckduckgo-web (#6020)

Extract the DuckDuckGo anti-abuse challenge solver + FE signals (CHALLENGE_STUBS,
countHtmlElements, buildHtmlLookup, sha256Base64, solveDuckDuckGoChallenge,
makeDuckDuckGoFeSignals) verbatim into the leaf duckduckgo-web/challenge.ts. The vm
sandbox + 5s timeout (SECURITY note) are preserved. Host imports back the two it uses.

Host 924 -> 788 LOC. Byte-identical bodies (verbatim 132/132), leaf does not import the
host (no cycle). The now-dead createHash/parse5 host imports are removed; vm stays (still
used in host). Auth/cookie/warm/seed/executor untouched. Adds a split-guard; consumer
tests stay green (duckduckgo-web-executor 15, duckduckgo-domain-4037 8).

* test(cli): deflake setup-claude.test.ts — silence console to stop stdout/report interleaving (#5959) (#6019)

Integrated into release/v3.8.44. Deflakes tests/unit/cli/setup-claude.test.ts (#5959) — verified in CI: setup-claude now passes in Unit Tests fast-path (2/2).

Merged with --admin over two PRE-EXISTING base-reds proven independent of this test-only change (this PR only touches setup-claude.test.ts + CHANGELOG):
- Fast Quality Gates → check:test-discovery: tests/unit/executors/{firecrawl-fetch,xai-executor}.test.ts are orphaned on release/v3.8.44 (added by #5793/#5800); the shard glob 'tests/unit/{api,...,ui}/**' omits 'executors'. Both blobs exist on the pristine base.
- Unit Tests fast-path (2/2): tests/unit/settings-i18n-keys.test.ts → 'direct translation calls have English messages' fails on the pristine base too (unrelated i18n base-red).

* fix(cli): stabilize setup-claude.test.ts flake — inject dry-run log sink (#6021)

* fix(cli): stabilize setup-claude.test.ts flake — inject dry-run log sink (#5959)

Root cause (isolated empirically, 5/10 fail on the pristine base): the
dry-run path of syncClaudeProfilesFromModels console.log's a multi-byte
box-drawing heading ("── [dry-run] … ──"). Under the node:test runner
that write lands on the test child's stdout and corrupts the runner's
V8-serialized event stream ~50% of the time ("Unable to deserialize
cloned data due to invalid or unsupported version"), killing the file at
the first logging test. ASCII-only logging never reproduced it (0/20);
the unicode heading alone reproduced it (10/20).

Fix: syncClaudeProfilesFromModels accepts an injectable log sink
(opts.log, CLI default unchanged: console.log). The dry-run test injects
a collector — keeping unicode off the child's stdout — and gains
assertions on the dry-run report (path + parsed settings content), which
FAIL on the old code (log ignored) and PASS on the new one.

Validation: 0/30 failures post-fix vs 5/10 pre-fix on the same tree.

Baselines: complexity 2003->2006 and cognitive 859->860 are inherited
post-3a3d618fe release drift — measured identical on the pristine base
with and without this change (notes added in both files).

* test(ci): collect the orphaned tests/unit/executors/ directory (base-red unblock)

#5800 created tests/unit/executors/ outside every unit-runner brace glob,
so its 2 test files (firecrawl-fetch, xai-executor) never ran anywhere and
check:test-discovery flags them as NEW orphans on the pristine base,
red-flagging every PR into release/v3.8.44. Added 'executors' to the
runner globs in package.json (7 scripts), ci.yml unit shards, quality.yml
TIA glob, build-test-impact-map.mjs, and the test-discovery gate's
COLLECTORS (the gate enforces those stay in sync). Both files pass when
actually collected (10/10); cli+executors under suite flags: 99/99.

* chore(quality): complexity baseline 2006 -> 2007 (CI-observed value)

The GitHub fast-gates runner measures 2007 where local measures 2006 —
the same local-vs-CI off-by-one documented in the 2026-06-26 note. Pin
the CI-observed value so the gate is deterministic where it runs.

* fix(i18n): add the 6 missing en.json keys flagged by settings-i18n-keys (base-red unblock)

providers.iconUrlLabel/iconUrlHint (referenced by AddCompatibleProviderModal
and EditCompatibleNodeModal) and settings.authz.cors.wildcard.title/desc
(the #5602 CORS_ALLOW_ALL banner in AuthzSection) shipped without their
en.json messages — 'direct translation calls have English messages' fails
on the pristine release tip, red-flagging every PR. git log -S proves the
keys never existed (not a merge-eat). Scanner test: 10/10 green.

* refactor(executors): extract reasoning-effort (base) + tool-normalization (codex) leaves (#6030)

Two pure-leaf follow-ups closing the Block H tail:

- base/reasoningEffort.ts: provider-aware reasoning_effort sanitation
  (MISTRAL/GITHUB reject patterns, supportsMaxEffortForProvider,
  sanitizeReasoningEffortForProvider). Deps are config/services only
  (PROVIDER_CLAUDE, isClaudeCodeCompatible, supportsClaudeMaxEffort/supportsXHighEffort)
  so the leaf never imports the host — no cycle. base.ts re-exports
  sanitizeReasoningEffortForProvider for its external importers (mimoThinking + tests).
  base.ts 1466 -> 1312 LOC.

- codex/tools.ts: Responses-API tool normalization (CODEX_HOSTED_TOOL_TYPES hosted-tool
  passthrough, isCodexFreePlan gating, normalizeCodexTools). Self-contained
  (console.debug only). codex.ts re-exports isCodexFreePlan + normalizeCodexTools for
  external importers (tests + provider services). codex.ts 1430 -> 1268 LOC.

Byte-identical bodies (verbatim: base 100/100, codex 126/126); both leaves have zero host
imports. Adds two split-guards asserting the leaf owns the symbol and both import paths
resolve to the same function. Consumer tests stay green (base-executor-sanitize-effort 34,
executor-codex 40, mimoThinking 9, codex-free-plan-image-generation 3, issue-fixes 6).

* test(ci): move orphaned executor tests to top-level so a runner collects them (#6027)

Integrated into release/v3.8.44 — collect orphaned executor tests (check:test-discovery base-red).

* test(cli): deflake cli-setup-opencode.test.ts — silence console (#5959-class landmine) (#6033)

The command under test prints CLI progress with multi-byte glyphs
(printSuccess "✔" in the happy paths, printError "✖" in the dist-missing
path that test 4 exercises) via console.log. Under the node:test runner
those child-stdout writes interleave with the V8-serialized report frames
and can corrupt the stream — the exact #5959 mechanism proven for
setup-claude.test.ts; this file's ✖ line was already visible entangled in
red CI runs. No test here asserts on stdout, so silence console.log/info/
warn for the file (same pattern as #6019/#6021, restored in after()).

Validation: pre-fix the ✖/✔ lines reach stdout every run (grep-able);
post-fix stdout is clean, 4/4 tests green, 0/20 failures across 20 runs.

* feat(agy): support Google Cloud project ID settings (#5905)

* feat(agy): support Antigravity project ID settings

* refactor(agy): collapse Antigravity family project gate

---------

Co-authored-by: Nikolay Alafuzov <alafuzov_nn@rusklimat.ru>

* feat(proxy): add Webshare proxy pool import and sync (#5993)

* feat(proxy): add Webshare proxy pool import and sync

Adds Webshare (https://proxy.webshare.io) as a fourth source in the
free-proxy provider framework alongside 1proxy, Proxifly, and IPLocate.
WebshareProvider paginates the account's `/api/v2/proxy/list/` endpoint
(Authorization: Token <key>), upserts proxies into the shared
`free_proxies` table via the existing db/freeProxies.ts helpers, and
tombstones proxies the account no longer lists (recycled/retired IDs)
while never touching rows already promoted into the live proxy pool.

Unlike the other sources, Webshare is a paid per-account list, so it is
gated on FREE_PROXY_WEBSHARE_API_KEY rather than a plain on/off flag.
No DB migration needed — reuses the existing free_proxies table and
proxy_registry-on-promote path.

Co-authored-by: ricatix <d.enistraju155@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/1176

* chore(changelog): restore release entries + add webshare bullet

---------

Co-authored-by: ricatix <d.enistraju155@gmail.com>

* feat(api-keys): add per-key device/connection tracking (#5998)

* feat(api-keys): add per-key device/connection tracking

Tracks distinct client devices (SHA-256 fingerprint of IP + User-Agent)
seen with each API key, with a 30-minute TTL and per-key/global caps. The
tracker is in-memory only (module-scoped Map, same pattern as
sessionManager.ts — no global.* singleton) and never stores the raw IP:
it is masked before being written.

Hooked into open-sse/handlers/chatCore.ts (the real chat entry) rather
than the legacy src/sse/handlers path. New GET /api/keys/[id]/devices
management route exposes masked device details for a key, and the
API Keys dashboard tab gets a "Devices" count badge alongside the
existing Sessions badge.

This is a new granularity distinct from the existing maxSessions cap
(src/lib/db/apiKeys.ts), which limits concurrent sticky-routing sessions
rather than tracking device identity.

Co-authored-by: Muhammad Mugni Hadi <mugni@rukita.co>
Inspired-by: https://github.com/decolua/9router/pull/931

* chore(changelog): restore release entries + add api-keys device-tracking bullet

---------

Co-authored-by: Muhammad Mugni Hadi <mugni@rukita.co>

* fix(providers): only apply openai-family model inference fallback when no cataloged provider serves the id (#5852) (#5938)

resolveModelByProviderInference() in open-sse/services/model.ts had an
unconditional /^gpt-/i heuristic that hijacked any model id starting with
gpt-/o1/o3 into provider openai, even when the id is cataloged under other
providers. This broke bare (non-combo) requests for open-weight models like
gpt-oss-120b (served by fireworks/cerebras/scaleway/byteplus/sambanova/
heroku), which don't exist on openai's catalog, producing a 404 with no
fallback.

Gate the heuristic on providers.length === 0 so it only fires for genuinely
uncataloged openai-family ids, letting cataloged ids fall through to the
existing single-candidate / ambiguous-candidate resolution paths.

Regression guard: tests/unit/gptoss-provider-inference-5852.test.ts

* fix(cc-compatible): send SSE accept for streamed requests (#5958)

Integrated into release/v3.8.44 — SSE Accept header for streamed cc-compatible requests (thanks @rdself).

* fix: deepseek-web reliability — auto-refresh on 401/403, refresh v2.0.0 client headers, fix token-kind bulk import (#5988)

Integrated into release/v3.8.44 — deepseek-web auto-refresh + v2.0.0 headers + token-kind bulk import (thanks @backryun).

* feat(providers): support Vercel AI Gateway embeddings and images (#5968)

* feat(providers): support Vercel AI Gateway embeddings and images

Extends the existing vercel-ai-gateway (alias vag) provider — currently
chat-only — with embeddings and image generation support, since the
gateway's OpenAI-compatible /v1 API also exposes /embeddings and
/images/generations. Adds entries to EMBEDDING_PROVIDERS
(embeddingRegistry.ts) and IMAGE_PROVIDERS (imageRegistry.ts) modeled
on the existing openai entries.

Out of scope for this PR (tracked as follow-ups): the /v1/credits
usage reader, retry:{429:2} tuning, and claude->reasoning_effort
mapping.

Co-authored-by: Ngô Tấn Tài <tantai@newnol.io.vn>
Inspired-by: https://github.com/decolua/9router/pull/1704

* chore(changelog): restore release entries + add vercel-gateway media bullet

---------

Co-authored-by: Ngô Tấn Tài <tantai@newnol.io.vn>

* feat(cli-tools): add Crush CLI tool to the dashboard (#5970)

* feat(cli-tools): add Crush CLI tool to the dashboard

Add a `crush` entry to the dashboard CLI-Tools catalog and a new
`/api/cli-tools/crush-settings` route (GET/POST/DELETE), cloned from the
`pi` tool's route as a template. OmniRoute already ships a `crush` CLI
command path (bin/cli/commands/setup-crush.mjs) but the dashboard catalog
had no matching entry.

The new route writes the real Crush config shape (providers.omniroute as
an openai-compat provider block) to the same canonical config path
(~/.config/crush/crush.json) that setup-crush.mjs's resolveCrushTarget()
already writes to, so the dashboard and the CLI command agree on one
location. Adds CLI_TOOL_RUNTIME_CONFIG.crush for detection/status, and
bumps EXPECTED_CODE_COUNT (18 -> 19) plus the catalog-count/schema tests
that enumerate the full tool list.

Co-authored-by: dopaemon <polarisdp@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/1233

* chore(changelog): restore release entries + add crush cli bullet

---------

Co-authored-by: dopaemon <polarisdp@gmail.com>

* feat(dashboard): suggest HuggingFace Hub media models (#5990)

* feat(dashboard): suggest HuggingFace Hub media models

MVP scope:
- imageRegistry.ts: add an image kind entry for the huggingface provider
  (HF Inference API text-to-image), with a dedicated "huggingface-image"
  format since the endpoint returns raw image bytes rather than JSON.
- New handler open-sse/handlers/imageGeneration/providers/huggingface.ts,
  wired into imageGeneration.ts's format dispatch.
- New pure helper module open-sse/services/hfModelSuggestions.ts: maps a
  dashboard media kind to an HF Hub pipeline_tag and sorts/limits raw HF
  Hub search results (unit-tested directly).
- New route GET /api/v1/providers/suggested-models proxies the public HF
  Hub models search API server-side (Zod-validated query, buildErrorBody
  on every error path, no HF token exposed client-side — this project has
  no server-side HF search token config, so it calls unauthenticated).
- UI: ImageExampleCard now fetches suggested HF Hub models for the
  huggingface provider and merges them into the model picker as a
  selectable chip row, alongside the existing static provider models list.
- i18n: adds media.suggestedModels to en.json only.

Co-authored-by: yicone <yicone@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/1633

* chore(changelog): restore release entries + add hf-hub media suggest bullet

---------

Co-authored-by: yicone <yicone@gmail.com>

* feat(dashboard): collapse and sort provider quota rows by remaining (#5977)

* feat(dashboard): collapse and sort provider quota rows by remaining

Sort the expanded quota list by remaining percentage (highest first)
and collapse it to the first 3 rows by default, with a "Show N more" /
"Show less" toggle when a connection reports more than 3 quotas. This
keeps the most at-risk quotas out of view below a long list of
healthy ones.

Extracts the sort/slice logic into pure helpers
(sortQuotasByRemaining, getVisibleQuotas) exported from
QuotaCardExpanded.tsx and unit-tests them directly.

Co-authored-by: CườngNH <j2.cuong@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/1919

* chore(changelog): restore release entries + add quota collapse/sort bullet

---------

Co-authored-by: CườngNH <j2.cuong@gmail.com>

* feat(providers): refresh The Old LLM (Free) model catalog (#5181)

* feat(dashboard): add tool-source diagnostics settings toggle (#5978)

* feat(dashboard): add tool-source diagnostics settings toggle

Adds a Settings > Advanced card (cloned from DebugModeCard) that lets
operators flip the existing `logToolSources` flag from the UI instead
of editing the DB row directly. The backend gate (chatCore.ts) and DB
default were already present but had no toggle. Also adds
`logToolSources` to the /api/settings Zod PATCH schema (it is `.strict()`,
so the key was previously rejected) and en-only i18n strings.

Co-authored-by: DuyPrX <93126969+DuyPrX@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/1825

* chore(changelog): restore release entries + add tool-source toggle bullet

---------

Co-authored-by: DuyPrX <93126969+DuyPrX@users.noreply.github.com>

* feat(oauth): import Codex connection from a raw ChatGPT access token (#5995)

* feat(oauth): import Codex connection from a raw ChatGPT access token

OmniRoute's only Codex import path (/api/oauth/codex/import) required both
access_token and refresh_token, leaving no import path for a user who only
has a bare ChatGPT website access token (no refresh token).

- src/lib/db/providers.ts: createProviderConnection gains an explicit
  authType "access_token" branch — intentionally never deduped (no stable
  long-lived identity to match on) — and derives the connection name from
  email/name the same way "oauth" does.
- src/lib/oauth/services/codexImport.ts: export extractCodexAccountInfo so
  the new import path reuses the existing JWT decode instead of duplicating
  one.
- New route POST /api/oauth/codex/import-token (Zod-validated body
  { accessToken, name? }); errors routed through buildErrorBody /
  sanitizeErrorMessage. The executor's refreshCredentials() already
  degrades safely to null when there is no refresh token, forcing re-auth
  on expiry instead of a refresh exchange.
- OAuthModal.tsx: the callback-URL manual-paste path for codex now detects
  an eyJ-prefixed pasted token and posts it to the new endpoint, mirroring
  the existing grok-cli raw-token paste pattern.

Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/1290

* chore(changelog): restore release entries + add codex token-import bullet

---------

Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>

* fix(resilience): parse Retry-After from 429 JSON body for cooldown (#5974)

Integrated into release/v3.8.44 — parse Retry-After from 429 JSON body for cooldown (incl. #6013 retry-after-json extraction by @KooshaPari).

* fix(embeddings): forward connection-level proxy to embedding requests (#5975)

Integrated into release/v3.8.44 — forward connection-level proxy to embedding requests.

* fix(api): guard shared API client against non-JSON error responses (#5973)

Integrated into release/v3.8.44 — guard shared API client against non-JSON error responses.

* feat(dashboard): surface Codex banked reset credits per account (#5199)

* feat(providers): add NVIDIA NIM image generation (#5971)

* feat(providers): add NVIDIA NIM image generation

NVIDIA already exists as a chat provider (integrate.api.nvidia.com,
OpenAI-compatible) but image generation is served on a different host
(ai.api.nvidia.com/v1/genai/<model>) with a native NIM body shape, so it
gets a dedicated `nvidia-nim` image format and handler rather than reusing
the OpenAI image path.

Adds the 4 FLUX models (flux.1-dev, flux.1-schnell, flux.1-kontext-dev,
flux.2-klein-4b) to IMAGE_PROVIDERS, plus handleNvidiaNimImageGeneration()
which shapes the per-model NIM request body (flux.1-dev's mode/cfg_scale
and 768-1344px/64px-increment dimension validation, flux.1-kontext-dev's
required input image + aspect_ratio, schnell/klein's optional array-form
edit image) and normalizes the NIM response (artifacts[]/images[]/data[]/
single-value shapes) into the OpenAI `{created, data}` shape.

Co-authored-by: eng2007 <aleksey.semenov@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/1195

* chore(changelog): restore release entries + add nvidia-nim image bullet

---------

Co-authored-by: eng2007 <aleksey.semenov@gmail.com>

* feat(providers): add Augment (Auggie CLI) local provider (#5972)

* feat(providers): add Augment (Auggie CLI) local provider

Adds a new local, no-auth provider that spawns the user's local `auggie`
CLI (`auggie --print --quiet --model <m> --`) and pipes a flattened prompt
via stdin, wrapping stdout as an OpenAI-compatible SSE stream or a single
chat.completion JSON body depending on the request's `stream` flag.

Auth is delegated entirely to `auggie login` outside OmniRoute — the
connection is registered `noAuth: true` and `refreshCredentials()` is a
no-op, matching the existing `NOAUTH_PROVIDERS` credential-less flow
(synthetic connection, no DB row required). An optional connection row is
still admitted via `FREE_APIKEY_PROVIDER_IDS` for display/priority
tracking, consistent with `opencode`. The dashboard "Test Connection"
flow spawns `auggie --version` to confirm the CLI is installed and
runnable, since there is no API key to validate upstream.

Security hardening (spawn is an untrusted-input sink):
- Command injection: spawn no longer passes `shell: true` on Windows. The
  binary is resolved to a concrete path/name and argv is handed straight to
  the OS loader, so no cmd.exe metacharacter interpretation is possible.
- Argument injection (flag smuggling): `model` is validated against the
  registry allowlist (`auggieProvider.models`) before any spawn — a model
  that is unknown or starts with "-" is rejected with a sanitized error and
  the subprocess is never started. A trailing `--` marks end-of-options in
  the argv as belt-and-suspenders.

Co-authored-by: chamdanilukman <16629923+chamdanilukman@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/1200

* test(golden): regenerate translate-path for auggie provider

---------

Co-authored-by: chamdanilukman <16629923+chamdanilukman@users.noreply.github.com>

* feat(providers): add ModelScope OpenAI-compatible provider (#5965)

* feat(providers): add ModelScope OpenAI-compatible provider

Ports ModelScope (Alibaba 魔搭) as a new API-key, OpenAI-compatible
provider — upstream 9router PR #1764. The upstream PR hardcoded
`https://api-inference.modelscope.ai/...` (`.ai` TLD); verified against
ModelScope's own API-Inference docs and third-party integration guides
that the real production domain is `api-inference.modelscope.cn`
(`.cn` TLD) and shipped that instead. Also drops the PR's static
5-model snapshot in favor of `passthroughModels: true` with an empty
seed list + `modelsUrl`, since ModelScope's open-model catalog moves
fast.

Updates the providers-constants-split characterization test's hardcoded
APIKEY_PROVIDERS count (159 -> 160) to match the new entry.

Co-authored-by: Umar Javed <114807145+tn5052@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/1764

* chore(changelog): restore release entries + add modelscope bullet

* test(golden): regenerate translate-path for modelscope provider

---------

Co-authored-by: Umar Javed <114807145+tn5052@users.noreply.github.com>

* feat(providers): add Qiniu OpenAI-compatible provider (#5966)

* feat(providers): add Qiniu OpenAI-compatible provider

Wires Qiniu (七牛云) AI inference gateway as a BYOK API-key provider.
Qiniu proxies many upstream models (DeepSeek V3/V4, Claude, Kimi and
more) behind a single key, so it ships with an empty static seed and
relies on passthroughModels + the live /v1/models catalog instead of a
single stale hardcoded model id.

- metadata: src/shared/constants/providers/apikey/gateways.ts
- registry entry: open-sse/config/providers/registry/qiniu/index.ts
  (format openai, executor default, bearer auth, baseUrl
  https://api.qnaigc.com/v1/chat/completions, modelsUrl
  https://api.qnaigc.com/v1/models)
- added to NAMED_OPENAI_STYLE_PROVIDERS so model import serves the live
  catalog and falls back to the (empty) local catalog on error, same
  pattern as the existing dgrid/zenmux/orcarouter gateways
- tests: tests/unit/qiniu-provider.test.ts (metadata, registry
  resolution, passthrough validation, live /v1/models fetch + fallback)

Co-authored-by: JiangZhuo <jiangzhuo@qiniu.com>
Inspired-by: https://github.com/decolua/9router/pull/911

* chore(changelog): restore release entries + add qiniu bullet

* test(golden): regenerate translate-path for qiniu provider

* test(providers): bump APIKEY count 160→161 for qiniu

---------

Co-authored-by: JiangZhuo <jiangzhuo@qiniu.com>

* feat(providers): add b.ai OpenAI-compatible provider (#5969)

* feat(providers): add b.ai OpenAI-compatible provider

Adds bai as a new OpenAI-compatible BYOK provider, distinct from the
existing thebai/theb.ai provider, using passthrough model discovery
(no hardcoded model list, live catalog served from api.b.ai/v1/models).

Co-authored-by: Delynn Assistant <zhen@dkzhen.org>
Inspired-by: https://github.com/decolua/9router/pull/963

* test(golden): regenerate translate-path for b.ai provider

* test(providers): bump APIKEY count 161→162 for b.ai

---------

Co-authored-by: Delynn Assistant <zhen@dkzhen.org>

* feat(providers): add Nube.sh OpenAI-compatible provider (#5936)

* feat(providers): add Nube.sh OpenAI-compatible provider

Nube.sh is a live BYOK OpenAI-compatible gateway (LiteLLM proxy) at
https://ai.nube.sh/api/v1, Bearer/API-key auth. Registered as an apikey
inference-host with an OpenAI-format, default-executor registry entry.

Its live model catalog is only reachable with a valid key
(/api/v1/models returns 401 unauthenticated), so no model IDs are
hardcoded — the entry uses passthroughModels + modelsUrl for live
enumeration instead of shipping unverifiable IDs.

Co-authored-by: whale9820 <87256750+whale9820@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/2294

* test(golden): regenerate translate-path for nube provider

* test(providers): bump APIKEY count 162→163 for nube

---------

Co-authored-by: whale9820 <87256750+whale9820@users.noreply.github.com>

* feat(providers): add Charm Hyper OpenAI-compatible provider (#5961)

* feat(providers): add Charm Hyper OpenAI-compatible provider

Registers Charm Hyper (hyper.charm.land) as a new API-key gateway
provider: OpenAI-compatible chat completions format, bearer auth,
free tier (100 monthly Hypercredits). Models are resolved via
passthrough (modelsUrl + live /v1/models import) instead of a
hardcoded upstream model list, since the specific model catalog is
not publicly documented.

Co-authored-by: whale <admin@dyntech.cc>
Inspired-by: https://github.com/decolua/9router/pull/2006

* test(golden): regenerate translate-path for charm-hyper provider

* test(providers): bump APIKEY count 163→164 for charm-hyper

---------

Co-authored-by: whale <admin@dyntech.cc>

* feat(providers): add SumoPod and X5Lab OpenAI-compatible providers (#5963)

* feat(providers): add SumoPod and X5Lab OpenAI-compatible providers

Both are OpenAI-compatible BYOK aggregator gateways, wired via the
default executor with bearer API-key auth. Neither ships a hardcoded
model list — both use passthroughModels with an empty seed list and a
live /v1/models fetcher, so the catalog always reflects what each
gateway actually serves instead of speculative model IDs.

- SumoPod: https://ai.sumopod.com/v1/chat/completions (sk- keys)
- X5Lab: https://api.x5lab.dev/v1/chat/completions (x5- keys)

Regression guard: tests/unit/sumopod-x5lab-provider.test.ts.

Co-authored-by: Rigel Ramadhani Waloni <rigel8911@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/1288

* chore(changelog): restore release entries + add sumopod/x5lab bullet

* test(golden): regenerate translate-path for sumopod + x5lab providers

* test(providers): bump APIKEY count 164→166 for sumopod + x5lab

---------

Co-authored-by: Rigel Ramadhani Waloni <rigel8911@gmail.com>

* feat(server): support reverse-proxy basePath deployment (#5992)

* feat(server): support reverse-proxy basePath deployment

Adds OMNIROUTE_BASE_PATH (opt-in, empty by default) to next.config.mjs
using Next.js's native basePath support so a deployment behind a
reverse-proxy subpath (e.g. https://host/omniroute/) works without
manual header stripping. Next.js strips the configured prefix from
nextUrl.pathname before route classification, so classifyRoute() and
isLocalOnlyPath() keep matching un-prefixed paths.

The two hardcoded auth redirect targets in
src/server/authz/pipeline.ts (root "/" -> "/dashboard" and
unauthenticated dashboard -> "/login") now prefix with
request.nextUrl.basePath so they stay inside the deployed subpath.
Default empty basePath is a no-op for existing root-path deployments.

Co-authored-by: zocomputer <help@zocomputer.com>
Inspired-by: https://github.com/decolua/9router/pull/1810

* docs(env): document OMNIROUTE_BASE_PATH in .env.example + ENVIRONMENT.md; restore changelog

* docs(env): document AUGGIE_BIN + CLI_AUGGIE_BIN (base-red from #5972 auggie)

---------

Co-authored-by: zocomputer <help@zocomputer.com>

* refactor(combo): extract buildTargetTimeoutRunner from handleComboChat (#6036)

Bloco J (hot-path decomposition), Task 1. Extract the per-target-timeout dispatch wrapper
(handleComboChat's handleSingleModelWithTimeout closure) verbatim into the leaf
combo/targetTimeoutRunner.ts as a factory buildTargetTimeoutRunner({handleSingleModel,
comboTargetTimeoutMs, log}). The per-model abort still comes from target.modelAbortSignal,
so the outer request signal is intentionally not a dependency. Host call-sites unchanged.

combo.ts shrinks ~60 LOC; leaf is 91 LOC (<800). Body byte-identical (verbatim), no cycle.
This is the first slice toward extracting the shared attempt-loop/success/error handlers
(Tasks 3-4) that de-duplicate handleComboChat and handleRoundRobinCombo. Adds a dedicated
test (5) so the failover path can be mutated independently. Consumer tests stay green
(combo-strategy-fallbacks 24, combo-499-abort 5, empty-content-failover 3, body-400-stop 1,
priority-quota-exhaustion 2, rr-streaming-lock 1, rr-session-stickiness 2).

Plan: _tasks/superpowers/plans/2026-07-03-blocoJ-combo-hotpath-decomposition.md

* feat(cli-tools): add CodeWhale CLI tool (#5996)

CodeWhale (https://github.com/Hmbown/CodeWhale) is the actively-maintained
successor to DeepSeek TUI — same author, renamed project. Added as a dual
entry alongside the existing "deepseek-tui" catalog entry (rather than a
hard rename) so users who still run the old DeepSeek TUI binary keep a
working dashboard card, while new users are steered to "codewhale".

New /api/cli-tools/codewhale-settings route writes the primary
~/.codewhale/config.toml and keeps an existing legacy
~/.deepseek/config.toml in sync (read fallback + best-effort write sync),
mirroring deepseek-tui-settings/route.ts. CLI_TOOLS and cliRuntime catalogs
updated; catalog cardinality tests/constants bumped accordingly (18→19
visible code tools, 28→29 total).


Inspired-by: https://github.com/decolua/9router/pull/1761

Co-authored-by: aristorinjuang <aristorinjuang@gmail.com>

* feat(i18n): auto-detect browser language on first visit (#5979)

* feat(i18n): auto-detect browser language on first visit

Adds a pure detectBrowserLocale() matcher (exact match, zh-HK/zh-MO
folded to zh-TW, language-prefix match, else null) plus a client-only
LocaleAutoDetect component mounted once in the root layout. On first
visit (no locale cookie set), it reads navigator.languages, computes a
match against the supported locales, and persists it via the same
cookie/localStorage writer LanguageSelector already used for manual
selection (now extracted to shared/lib/persistLocale.ts) before
refreshing the router.

Co-authored-by: anmingwei <anmingwei@dobest.com>
Inspired-by: https://github.com/decolua/9router/pull/1324

* chore(changelog): restore release entries + add browser-lang-detect bullet

---------

Co-authored-by: anmingwei <anmingwei@dobest.com>

* fix(dashboard): render Update-now API errors as text, not the raw envelope object (#5991) (#6028)

Integrated into release/v3.8.44 — fix(dashboard) render Update-now API errors as text, not the raw envelope object (#5991).

Merged with --admin: the fix is a one-line frontend change funneling the error body through the already-tested extractApiErrorMessage() helper, guarded by tests/unit/ui/home-update-error-render-5991.test.ts (3/3 pass, 3/3 fail on pre-fix source). The release branch is under a heavy parallel-merge storm (tip advanced ~6× mid-CI), so the branch is synced to the latest tip and landed atomically to avoid perpetual CONFLICTING; unit-shard reds seen earlier were pre-existing base-reds/flakes unrelated to this source-scan-only change.

* feat(api): expose provider plugin manifest (#6001)

* feat(api): expose provider plugin manifest

* test(translator): split responses chat request coverage

* test(mutation): register provider coverage tests

* feat(api): expose provider plugin manifest

* fix(ci): fail closed for prerelease latest promotion

* chore(ci): reconcile provider manifest complexity gate

* feat(api): expose provider plugin manifest

* test(translator): split responses chat request coverage

* test(mutation): register provider coverage tests

* fix(ci): fail closed for prerelease latest promotion

* chore: rebase onto release tip; drop out-of-scope translator test split + promote-script tweak

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* docs(changelog): add provider plugin manifest entry

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* chore(stryker): register account-fallback-retry-after-json test (base-red)

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

---------

Co-authored-by: kooshapari <kooshapari@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(providers): add CN sign-up geo-restriction notices for SenseNova & StepFun (#5462)

* feat(sidecar): advertise provider manifest url (#6007)

* feat(sidecar): advertise provider manifest url via X-OmniRoute-Provider-Manifest-Url header

Re-cut onto release tip: manifest-url feature only (dropped stale-base noise).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* docs(changelog): add sidecar manifest-url entry

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* chore(complexity): rebaseline 2009->2015 (inherited release-tip drift; feature adds 0)

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

---------

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(autoCombo): latency/speed-optimized routing mode + omniroute_pick_fastest_model MCP tool (#6011)

* feat(autoCombo): latency/speed-optimized routing mode + omniroute_pick_fastest_model MCP tool

* test(translator): split responses chat request coverage

* refactor(mcp): extract fastest-model tool modules

* fix(i18n): cover provider icon and cors labels

* test(mutation): register latency coverage files

* test(ci): collect executor unit tests

* refactor(ci): reduce latency path complexity

* fix(mcp): include models catalog module

* feat(autoCombo): latency/speed-optimized routing + omniroute_pick_fastest_model MCP tool

Re-cut onto release tip: keep speed-routing + MCP tool + supporting catalog split;
drop out-of-scope translator split, en.json/ci.yml/package.json orphans, and unrelated
proxyFetch/responsesStreamHelpers/tokenLimitCounter refactors.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

---------

Co-authored-by: kooshapari <kooshapari@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* docs(changelog): restore #5181/#5199/#5462 feature bullets eaten by merge

* feat(usage): on-demand period-scoped usage-data reset (re-cut onto release tip) (#5831)

* chore(quality): rebaseline eslintWarnings 4199->4256 + cognitiveComplexity 860->861 (v3.8.44 cycle drift)

Inherited v3.8.44 cycle drift measured on release tip 72ee80649 by the release-green
pre-flight during the /review-prs fix-batch round. The Quality Ratchet does NOT run on
PR->release fast-gates, so eslint warnings + cognitive complexity accrue unmeasured
across the cycle. Cyclomatic complexity is already green (2012 < baseline 2015) and
needs no bump. Each value carries a dated justification note; no production code touched.

* feat(claude-code): opt-in auto-permission classifier compat mode (re-cut onto release tip) (#5810)

* feat(providers): client-identity header profiles for compatible nodes (re-cut) + forbid cookie in custom headers (#5812)

* docs(openapi): document 9 newly-added routes to restore coverage ratchet (v3.8.44)

Documents the routes added this cycle that dropped openapiCoverage 36.9%->36.2%
below the ratchet baseline: 2 public v1 endpoints (/v1/ocr Mistral-OCR-compatible,
/v1/audio/translations Whisper-compatible) with full request/response specs, plus 7
dashboard/CLI-local routes marked x-internal:true (suggested-models, provider-plugin-
manifest, keys/{id}/devices, settings/purge-usage-history, oauth/codex/import-token,
cli-tools crush-settings + codewhale-settings). Coverage 36.2%->37.8% (207/547),
above baseline 36.9. check:openapi-routes/security-tiers/fabricated-docs all pass.

* refactor(sse): decompose handleComboChat auto-strategy region (Block J Task 2 — parseAutoConfig + resolveAutoStrategyOrder) (#6049)

* refactor(sse): extract pure parseAutoConfig leaf from handleComboChat

Block J Task 2 (safe slice): the auto-strategy config-resolution block in
handleComboChat is a pure function of (combo, eligibleTargets) with no side
effects, no early returns and no mutation. Extract it verbatim into
open-sse/services/combo/autoConfig.ts::parseAutoConfig so the god-function
shrinks and the derivation is independently unit-testable.

Behavior is byte-identical (verbatim-audited); combo.ts 3309->3280 LOC.
Adds tests/unit/combo-auto-config-split.test.ts (5 cases) pinning the
strategy-precedence, candidate-pool, weights and fallback derivations.

* refactor(sse): extract resolveAutoStrategyOrder leaf from handleComboChat

Block J Task 2 (coupled slice): the ~215-line `if (strategy === "auto")`
branch of handleComboChat is extracted into
open-sse/services/combo/resolveAutoStrategy.ts::resolveAutoStrategyOrder.

The branch is a control-flow region (mutates orderedTargets +
autoUsedExplicitRouter, early-returns 429, side-effect _registerExecutionCandidates),
so it is not a pure byte-identical move: the two `return unavailableResponse(...)`
exits become `{ earlyResponse }` and the mutated locals are returned instead of
closed over. Every other logic line is verbatim (semantic diff = only those
wrappers + the deeper getLKGP import path). `buildAutoCandidates` lives in
combo.ts, so it is injected via deps to keep the leaf acyclic (same DI pattern as
buildTargetTimeoutRunner) — which also makes the branch independently testable.

combo.ts 3280->3065 LOC. typecheck:core + check:cycles clean; dead host imports
removed. 60/60 consumer tests (router-strategies / auto-combo-engine /
combo-strategy-fallbacks / scoring-clamp / candidate-expansion / hidden-models)
cover the routable path end-to-end; new tests/unit/combo-resolve-auto-strategy-split.test.ts
pins the DI contract + the early-429 and default-ordering exits.

* test(sse): point quota-bypass source scan at resolveAutoStrategy leaf

The 'auto combo disables hard provider quota cutoffs when relay requests bypass'
source scan asserted combo.ts contains the bypass logic
(relayOptions?.bypassProviderQuotaPolicy === true + quotaPreflight enabled:false).
That block was extracted verbatim into combo/resolveAutoStrategy.ts (Block J
Task 2), so the scan now reads the leaf. Behavior unchanged.

* fix(ci): release-green base-reds — #5695 test regex + file-size rebaseline (#6093)

- tests/unit/ui/quick-start-api-keys-link-5695.test.ts: tolerate Prettier
  splitting <Link href=...> across lines (\s+) so the step1Desc regex matches
  the multi-line /dashboard/api-manager Link instead of skipping to step2's
  single-line /dashboard/providers Link. Code is correct; the test was brittle.
- config/quality/file-size-baseline.json: rebaseline 5 files that grew via
  already-merged PRs on the release tip (ApiManagerPageClient 3017->3058,
  OAuthModal 969->989, cliRuntime 1090->1100, webProvidersA 805->809,
  deepseek-web.test 1081->1092). Dated note added; shrink tracked in #3501.

* fix(translator): wrap Kiro system prompt in <system-reminder> (port from 9router#2306) (#6053)

Kiro/CodeWhisperer has no system role, so system messages were normalized to a
user turn with no wrapper — the full Claude Code system prompt then appeared as
raw user text, polluting the model context. Wrap system-origin content in
<system-reminder> tags before merging it into the Kiro user message. Real user
turns are unaffected. Existing history-merge tests aligned to the wrapped value.

Reported-by: VitzS7 (https://github.com/decolua/9router/issues/2306)

* fix(translator): strip multipleOf from antigravity/gemini tool schemas (port from 9router#2309) (#6052)

`multipleOf` is not part of the Gemini/antigravity OpenAPI 3.0 schema subset, so
leaving it in function_declaration parameters triggered a hard upstream 400
("Unknown name multipleOf"). Add it to GEMINI_UNSUPPORTED_SCHEMA_KEYS so it is
stripped at every schema level; minimum/maximum stay (Gemini accepts them).

Reported-by: abil0321 (https://github.com/decolua/9router/issues/2309)

* fix(kimi-web, qwen-web): align model catalog with live /models + map scenario per model (#5915)

* fix(kimi-web): align catalog with live models

Update the kimi-web catalog and request scenario selection to match
www.kimi.com's live GetAvailableModels response.

* fix(qwen-web): stop aliasing qwen3-coder-plus

Keep qwen3-coder-plus as its own model because it is present in the
live Qwen web models catalog.

* feat(minimax): extract M3 <think> to reasoning_content on OpenAI-format tiers (#6050)

MiniMax M3 is registered with format:"openai" on 8 provider tiers (trae,
huggingchat, bazaarlink, ollama-cloud, opencode, cline, opencode-zen,
codebuddy-cn), where its raw <think>...</think> tags leaked directly into
`content` instead of surfacing as a separate `reasoning_content` field.

OmniRoute already has the extraction primitive
(extractThinkingFromContent in responseSanitizer/reasoning.ts); it was just
gated to deepseek-r1/r1-distill/qwq. Extend the allowlist
(isTextualReasoningTagNativeRoute) with a minimax-m3-only pattern, excluding
the two direct minimax/minimax-cn tiers, which stay on Anthropic's Messages
format (targetFormat: "claude") and already surface reasoning natively.


Inspired-by: https://github.com/decolua/9router/pull/2231

Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: zmf963 <19422469+zmf963@users.noreply.github.com>

* fix: unwrap Cline response envelope (#6046)

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* refactor(sse): extract applyStrategyOrdering leaf from handleComboChat (Block J Task 3) (#6063)

* refactor(sse): extract applyStrategyOrdering leaf from handleComboChat

Block J Task 3: the ~177-line else-if chain covering every non-auto combo
strategy (lkgp / strict-random / random / fill-first / p2c / least-used /
cost-optimized / reset-aware / reset-window / context-optimized / headroom /
quota-share) is extracted into
open-sse/services/combo/applyStrategyOrdering.ts::applyStrategyOrdering.

Each branch only reorders orderedTargets (no early returns, no other mutable
state), so the extraction is a clean verbatim move returning the reordered list;
the host replaces the chain with `else { orderedTargets = await
applyStrategyOrdering(strategy, orderedTargets, deps); }`. Semantic diff vs the
original chain = only the leading `if` (was `} else if`), the trailing return
and the deeper getLKGP import path — no logic line changed. None of the 13 strategy
helpers live in combo.ts, so no DI/cycle (unlike the auto branch).

combo.ts 3065->2883 LOC (3309->2883 across Task 2+3). typecheck:core + check:cycles
clean; 9 dead host imports removed (targetSorters block emptied). 47/47 consumer
tests (router-strategies / combo-strategy-fallbacks / rr-session-stickiness /
tag-routing) cover the DB-backed branches end-to-end; new
tests/unit/combo-apply-strategy-ordering-split.test.ts pins random / fill-first /
unknown exits.

* test(sse): point #2359 modelStr-guard scans at applyStrategyOrdering leaf

The LKGP fallback + non-auto strategy ordering (the two target.modelStr string-
method call sites) were extracted verbatim from combo.ts into the
applyStrategyOrdering leaf (Block J Task 3). The #2359 source scans now read the
leaf that owns those usages; the guard and the no-unguarded-usage assertions are
unchanged in intent.

* chore(ci): scan combo strategy leaves in check:known-symbols

Block J decomposed the combo dispatch: the `strategy === "..."` branches for
the 12 non-auto strategies moved to combo/applyStrategyOrdering.ts and the auto
branch to combo/resolveAutoStrategy.ts. The known-symbols gate previously scanned
only combo.ts, so it would report those strategies as canonicalNotHandled. Scan
all three dispatch files. Verified: 18/18 canonical strategies via dispatch.

* fix(combo): fallback to sibling model on 500 for per-model-quota providers (#5976)

* fix(combo): fallback to sibling model on 500 for per-model-quota providers

Two issues prevented combo fallback when gemini/gemma-4-31b-it returned 500:

1. targetExhaustion: connection-level exhaustion marked the shared gemini
   connection as exhausted, skipping the sibling model (gemma-4-26b-a4b-it).
   Skip markConnectionLevelExhaustion for per-model-quota providers (gemini,
   github, passthrough, compatible) since a model-level 500 does not mean
   the connection is bad.

2. combo retry loop: the auth layer records a model lockout on 500, but the
   retry loop did not check isModelLocked before retrying — it retried the
   same locked model instead of falling back. Add isModelLocked guard before
   the transient-retry decision.

* fix tests timeout

* fix: clear quota fallback CI gates

* quality-gate: extract test SSE stream helpers

* drop scope creep

* fix(combo): retry sibling models only on 500 errors

* fix(combo): reconcile onto release/v3.8.44 — keep targetExhaustion 500 fix, drop slow integration test

Reconciled by maintainer onto the current release tip:
- kept the core fix (targetExhaustion.ts model-500 guard for per-model-quota
  providers + the isModelLocked retry early-return in combo.ts) and its unit test
- dropped tests/integration/combo-concurrent-failure-recovery.test.ts +
  _sseTestHelpers.ts: they use Math.random()-based delays and 30s timeouts, run
  >3min and are flake-prone in the test:integration CI job; the unit test
  (tests/unit/combo/combo-target-exhaustion.test.ts, 21 cases) fully covers the fix
- CHANGELOG entry added

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

---------

Co-authored-by: Koosha Pari <kooshapari@gmail.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com>
Co-authored-by: hartmark <hartmark@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(xai): surface Grok usage on quota dashboard via local usageHistory aggregation (#5806)

xAI has no public per-account quota API (the billing console requires a
session cookie, not an API key). Add getXaiUsage(connectionId), mirroring
the existing Xiaomi MiMo self-track pattern: sum tokens routed to the
connection from usage_history via getMonthlyProviderTokensForConnection
and surface them as a cumulative, uncapped quota (unlimited: true,
remaining: 100 — xAI has no fixed monthly cap). Register 'xai' in
USAGE_FETCHER_PROVIDERS and wire a switch case in getUsageForProvider.


Inspired-by: https://github.com/decolua/9router/pull/2150

Co-authored-by: ron <devestacion@gmail.com>

* feat(services): add Mux managed embedded service (#6034)

Adds Mux (coder/mux — local agent-orchestration daemon) as a fourth-tier
embedded service built on the existing ServiceSupervisor framework, the
same shape as 9Router and CLIProxyAPI:

- Installer (src/lib/services/installers/mux.ts): npm install/update via
  runNpm (array args + env-based prefix, no shell interpolation), modeled
  on ninerouter.ts. Mux ships an npm package (`mux`) with a documented
  headless `mux server --host <host> --port <port>` mode, so no
  git-clone+build path was needed.
- Registered in bootstrap.ts (SERVICES[] + buildSpawnArgsFactory).
- DB seed migration 113 (version_manager row, not_installed/auto_start=0).
- 7 API endpoints under /api/services/mux/ (install/start/stop/restart/
  update/status/auto-start) plus the shared [name]/logs SSE endpoint,
  mirroring the cliproxy route shape and delegating errors through
  createErrorResponse().
- Dashboard tab (MuxServiceTab) reusing ServiceStatusCard,
  ServiceLifecycleButtons, AutoStartToggle, ServiceLogsPanel.
- Docs: EMBEDDED-SERVICES.md (service table, architecture diagram, API
  reference, key-injection section), openapi.yaml, ENVIRONMENT.md,
  .env.example.

Security:
- Every /api/services/mux/* route is covered by the existing
  LOCAL_ONLY_API_PREFIXES "/api/services/" prefix (Hard Rule #17);
  added an explicit isLocalOnlyPath regression test for all 8 routes.
- Mux binds to 127.0.0.1 explicitly (never 0.0.0.0) as defense-in-depth,
  since it orchestrates AI agents that can execute host commands.
- The bearer token is generated the same way as 9Router's key
  (getOrCreateApiKey) and injected via MUX_SERVER_AUTH_TOKEN (mux's
  documented env form) rather than a CLI flag, so it never appears in
  `ps`/process listings.
- No shell interpolation anywhere in the installer (Hard Rule #13): all
  npm/spawn args are static arrays; the install prefix and auth token
  travel via the env option.


Inspired-by: https://github.com/decolua/9router/pull/1802

Co-authored-by: Ansh7473 <Ansh7473@users.noreply.github.com>

* feat(services): promote Bifrost to embedded/supervised service (#5670) (#5817)

Promotes Bifrost (@maximhq/bifrost — Go AI-gateway) from an env-only relay
sidecar to a first-class embedded/supervised service, matching the existing
cliproxy/9router model. Implements item #2 of #5670; the broader RouterBackend
contract (items #1, #3-#5) stays out of scope.

- Installer (npm-style, ninerouter model): install/update/getInstalledVersion/
  getLatestVersion (1h cache)/resolveSpawnArgs (Go single-dash flags, pinned
  BIFROST_TRANSPORT_VERSION), needsApiKey=false
- Bootstrap SERVICES entry (healthPath /v1/models) + spawn-args factory branch
- Migration 113 seeds the version_manager row (not_installed, port 8080,
  auto_update=1, provider_expose=1)
- 7 lifecycle API routes under /api/services/bifrost/ (verbatim from cliproxy,
  errors sanitized) — loopback-only via existing LOCAL_ONLY_API_PREFIXES
- Shared [name]/logs branch for bifrost
- Dashboard tab + registration in the services page shell
- Relay auto-wiring: getBifrostRoutingConfig defaults BIFROST_BASE_URL to the
  supervised port when the instance is running; explicit env still wins; the
  env-only relay path (/v1/relay/.../bifrost) stays unchanged (compat layer)
- Docs (EMBEDDED-SERVICES, openapi) + unit tests (installer/route-guard/routing,
  19 tests) + RUN_SERVICES_INT-gated integration lifecycle

Note: the actual Go-binary install/start/health path requires a documented VPS
live-test before merge (Hard Rule #18 / spec section 7); the gated integration
harness is the vehicle for that run.

* fix(ci): document BIFROST_PORT to clear env-doc-sync base-red

The Bifrost embedded-service merge referenced process.env.BIFROST_PORT
(src/lib/services/bootstrap.ts, default 8080) without adding it to
.env.example / ENVIRONMENT.md, so check:env-doc-sync failed on the release
tip and reddened Fast Quality Gates for every open PR->release. Docs-only.

* fix(providers): emulate OpenAI tool_calls in GitLab Duo executor (#6051) (#6111)

Co-authored-by: felssxs <felssxs@users.noreply.github.com>

* fix(providers): strip orphan tool_result on Antigravity MITM path (#6026) (#6115)

* fix(registry): update grok-cli model context lengths (#5913)

grok-build 128k→256k, grok-composer-2.5-fast 128k→200k to match actual Grok CLI /context capacities so context-aware routing stops filtering these models out. Registry-only.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(proxy): batch delete, auto-test, health scheduler + transitive alias fix (#5918)

Proxy-registry batch management (batch-delete, auto-test, background health scheduler) + fix resolveProviderAlias to follow the alias chain transitively (oc -> opencode -> opencode-zen). Probe target now operator-configurable via PROXY_HEALTH_TEST_URL. Scope-creep files from the original branch dropped.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* feat(minimax): extract M3 reasoning_content on OpenAI-format tiers (#6073)

MiniMax M3 leaks raw <think>...</think> into content on 8 OpenAI-format provider tiers; extract it into reasoning_content, leaving the direct minimax/minimax-cn (Claude-format) tiers untouched. Replacement for the stale #5804 branch.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(ci): harden provider translate-path golden across CI runners (#6076)

Normalize OS/arch-derived request headers (X-Stainless-Os/Arch, (OS;arch) UAs, and Antigravity's os.platform()-derived platform substring) in the golden so the test is runner-independent. Fixes the Mac-literal Antigravity UA that would have failed on Linux CI. Supersedes stale #6002.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* test(embeddings): pin seeded connection to direct egress in route-edge-coverage (#5975 collateral)

#5975 made the embeddings service honor the connection-level proxy. The pre-existing
route-edge-coverage embeddings edge-case tests seed an openai connection while the
settings-proxy suite has left a provider-level proxy (provider.local:8080) in the shared
DATA_DIR that resetStorage() does not clear — inert before #5975, but now the leaked
proxy fast-fails the embedding upstream with PROXY_UNREACHABLE.

These tests do not exercise proxying, so seedOpenAIConnection now pins the connection to
proxyEnabled:false, making resolveProxyForConnection return a direct egress regardless of
leaked global proxyConfig. No assertions weakened; 16/16 in the file pass. Regression
surfaced by the concurrency=1 full-suite run; passes on #5975's parent, red after it.

* fix(config): externalize ws for copilot-m365-web executor (#6130, closes #6062)

Re-lands the #6098 ws-externalization fix onto release/v3.8.44 (it had merged to main by mistake and was reverted). Externalize ws/bufferutil/utf-8-validate so the copilot-m365-web WebSocket masking path works at runtime.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(providers): update Perplexity Web models (#6106)

Refresh the Perplexity Web model catalog + mode/model_preference mappings to the current live set. Regression guard: perplexity-web.test.ts.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(providers): update Gemini Web cookies and models (#6095)

Refresh Gemini Web cookie handling + model catalog. Regression guard: gemini-web.test.ts.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(models): normalize GLM-5.2 provider context (#6091)

Hosted GLM-5.2 provider aliases now respect their declared context caps instead of inheriting the native 1M; native/bare + verified OpenCode/ZenMux routes stay at 1M. Regression guards added.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(combo): prefer known context capacity over unknown (#6088)

When a combo filters a target for exceeding a known context limit, prefer remaining known-compatible targets over unknown-metadata ones. Regression guard: combo-context-window-filter.test.ts.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix: keep Claude tool results adjacent (#6035)

Reattach OpenAI tool_result adjacent to tool_use before Claude send (#6026). Integrated into release/v3.8.44.

* fix(security): persist IP filter config + enforce it in the authz pipeline (#6131) (#6132)

Integrated into release/v3.8.44 — IP filter persistence + authz-pipeline enforcement (closes #6131). HARD-neutro: validate-release-green on the merge shows the same 3 pre-existing base-reds as the release baseline (test-masking cycle-wide, unit red-herring, integration batch-E2E env); #6131's own tests + ip-filter/pipeline suites all green.

* fix(codex): use access_token.exp instead of id_token.exp for import expiresAt (#6075) (#6084)

Prefer access_token.exp over id_token.exp for Codex auth import (#6075). Integrated into release/v3.8.44.

* fix(compression): send patch-only to PUT /api/settings/compression in CompressionHub (#6039) (#6077)

Send patch-only to PUT /api/settings/compression in CompressionHub (#6039). Integrated into release/v3.8.44.

* fix: reqId ReferenceError in safety-net redirect, dead code, filename typo (#6097)

Fix reqId ReferenceError in safety-net combo redirect + dead-code + DESING→DESIGN rename. Integrated into release/v3.8.44.

* fix(combo): expand fingerprint-based providers into per-fingerprint combo targets (#6082)

Expand fingerprint-based providers into per-fingerprint combo targets. Integrated into release/v3.8.44.

* fix(auth): persist quota preflight account lockouts (#6090)

Persist quota preflight account lockouts until reset window. Integrated into release/v3.8.44.

* fix(combos): expand OpenCode/MiMo fingerprint accounts in combo builder (#6087) (#6092)

Expand OpenCode/MiMo fingerprint accounts in combo builder (#6087). Integrated into release/v3.8.44.

* chore(quality): rebaseline v3.8.44 release-green drift (eslint/cognitive/cyclomatic/file-size)

Measured on release tip 32e4c906e during the #6131/#5975 release-green pass:
eslintWarnings 4256->4270 (+14), cognitiveComplexity 861->867 (+6), cyclomatic
count 2015->2026 (+11), and testFrozen caps for models-catalog-route (1507->1600),
perplexity-web (959->999), route-edge-coverage (1234->1241, my #5975 comment +7).
Inherited cycle drift (the Quality Ratchet does not run on PR->release fast-gates);
compression 'bun not found' is a local-env false and codeql is within baseline, so
neither is rebaselined. No production code touched.

* fix(accountFallback): persist per-account 429 cascade + classify 'Monthly usage limit. Resets in N days.' (#6061)

Persist per-account 429 cascade + classify 'Monthly usage limit. Resets in N days'. Integrated into release/v3.8.44.

* feat(build): backend-only fast build (skip the dashboard frontend) (#6119)

Backend-only fast build (skip dashboard frontend). Integrated into release/v3.8.44.

* fix(provider-limits): clear transient rate-limit state when quota recovers (#6128)

Clear transient rate-limit state when quota recovers. Integrated into release/v3.8.44.

* docs: Normalize mixed-language documentation content (#6105)

Normalize mixed-language documentation to English. Integrated into release/v3.8.44.

* chore docs

* i18n(zh-CN): translate CHANGELOG entries and section headings (#6043)

Adopt zh-CN as a translated locale: translate CHANGELOG + supporting docs. Integrated into release/v3.8.44.

* chore(quality): rebaseline residual eslint + file-size drift (v3.8.44)

Residual drift on release tip 716041223 (moving target): eslintWarnings 4270->4279
(+9 as the branch advanced past the prior rebaseline) and testFrozen/frozen file-size
caps for providerLimits.ts (955->982), accountFallback.ts (1790->1864) and
sse-auth.test.ts (1553->1600). All inherited from parallel-session merges (e.g. #6128);
the two production god-files ideally warrant decomposition rather than a bump (tracked
as debt). No production code touched.

* fix(repo): remove Windows case-conflicting DESIGN duplicate (#6140)

Remove stale root DESIGN.md (Windows case-conflict with design.md). Integrated into release/v3.8.44.

* fix(provider-limits): close TOCTOU race in quota recovery clear (I2) (#6139)

Close TOCTOU race in quota recovery clear via CAS primitive (I2 from #6128). Integrated into release/v3.8.44.

* fix(glm): suppress </think> close marker leak in GLM Anthropic transport (#6133)

Suppress </think> close-marker leak in GLM Anthropic transport. Integrated into release/v3.8.44.

* fix(cli): give setup-claude a fallback profile generator like setup-codex (#6138)

Give setup-claude a fallback profile generator like setup-codex. Integrated into release/v3.8.44.

* fix(onboarding): route provider-details link by node id, not provider slug (#6145) (#6145)

Route onboarding provider-details link by node id (#6145). Integrated into release/v3.8.44.

* fix(translator): strip Responses-only truncation field before Chat Completions forwarding (#6109)

Strip Responses-only truncation field before Chat Completions forwarding (#2311). Integrated into release/v3.8.44.

* fix(mitm): guard against concurrent MITM server starts (#6107)

Guard against concurrent MITM server starts (#2316). Integrated into release/v3.8.44.

* feat(models): add claude-sonnet-5 to Antigravity catalog (#6103)

Add claude-sonnet-5 to Antigravity catalog. Integrated into release/v3.8.44.

* fix(providers): strip thinking param for minimax-m2.7 on NVIDIA NIM (#6102)

Strip unsupported thinking param for minimax-m2.7 on NVIDIA NIM. Integrated into release/v3.8.44.

* feat(providers): add Kenari OpenAI-compatible gateway (#6104)

Add Kenari OpenAI-compatible gateway (BYOK). Integrated into release/v3.8.44.

* feat(sse): per-request Auto-Combo controls (X-OmniRoute-Mode / X-OmniRoute-Budget) — closes #6023 #6024 #6025 (#6057)

Per-request Auto-Combo controls (X-OmniRoute-Mode / X-OmniRoute-Budget). Integrated into release/v3.8.44.

* feat(resilience): throttle concurrent upstream quota fetches — closes #6009 (#6058)

Throttle concurrent upstream quota fetches (#6009). Integrated into release/v3.8.44.

* fix(oauth): graceful 400 for keychain-import-only providers (zed) (#6041) (#6054)

Graceful 400 for keychain-import-only providers on OAuth route (zed, #6041). Integrated into release/v3.8.44.

* fix(dashboard): resolve broken Card import breaking next build (base-red from #6061) (#6155)

* fix(dashboard): resolve broken Card import breaking next build (base-red from #6061)

CoolingConnectionsPanel imported `Card` from `@/components/ui/card`, a path
that does not exist in this repo (there is no shadcn-style `src/components/ui/`).
The PR->release fast-gates do not run `next build`, so the broken import slipped
in and `next build` failed with:

  Module not found: Can't resolve '@/components/ui/card'

Fix: the <Card> here was only a styled container, so replace it with a <div>
carrying the equivalent Tailwind classes (border/bg/padding + rounded-card
shadow-sm). Also normalize the file from CRLF to LF (it shipped with CRLF).

Adds a vitest/jsdom regression test (tests/unit/ui/CoolingConnectionsPanel.test.tsx)
that fails-without-fix (Vite: 'Failed to resolve import @/components/ui/card')
and passes with it, plus renders/empty-state coverage. Rule #18.

* fix(dashboard): stop client CoolingConnectionsPanel dragging server DB barrel into browser bundle

Second base-red from #6061, surfaced once the broken Card import was fixed:

  ./node_modules/ioredis/built/connectors/StandaloneConnector.js
  Module not found: Can't resolve 'net'
  Import trace: ioredis <- rateLimiter.ts <- apiKeys.ts <- @/lib/localDb
                <- CoolingConnectionsPanel.tsx (a "use client" component)

The client panel imported `formatResetCountdown` from `@/lib/localDb` — the
server-side DB re-export barrel — which transitively pulls better-sqlite3/ioredis
(node:net) into the browser bundle. That violates the CLAUDE.md rule 'never
barrel-import from localDb'.

`formatResetCountdown` is a pure date-formatting function, so move its
implementation to the client-safe `@/shared/utils/formatting` (alongside
formatTime/formatDuration) and re-export it from db/providers/rateLimit.ts for the
existing server callers + barrel. The panel now imports it directly from the
shared util — no server code in the client bundle.

Tests (Rule #18):
- tests/unit/format-reset-countdown.test.ts (node:test, blocking test:unit) —
  pure-function coverage: null/past/invalid, s, m+s, h+m, ISO string.
- tests/unit/ui/CoolingConnectionsPanel.test.tsx mock updated to the new module.

* fix(release): v3.8.44 Phase-0 pre-flight — base-red sweep + ratchet absorption

- fix(models): stop resolveProviderAlias at registered provider ids so oc/
  reaches the no-auth opencode provider again (#2901 contract, regressed by
  #5918's transitive chain; transitivity kept across alias-only hops)
- fix(auggie): handle async EPIPE 'error' events on child stdin so a
  fast-exiting CLI surfaces a sanitized error instead of crashing (both
  spawn sites); deflakes auggie-executor tests
- test: align provider family count 166->167 (Kenari #6104), regenerate
  translate-path golden on Linux (+kenari), opencode quota scope
  provider->connection (#6061)
- quality(test-masking): add _deletedWithReplacement allowlist support to
  check-test-masking.mjs (deletion exempt ONLY when the declared replacement
  test exists in HEAD; 5 new gate unit tests) + reduction allowlist entries
  for the verified #5958/#6088/#5816 migrations + targetExhaustion->
  combo-target-exhaustion replacement (#5976, 21 cases/52 asserts vs 13/37)
- quality(file-size): absorb v3.8.44 cycle drift (oauth route 960,
  providerLimits 998, chat 1662, auth 2426) with justification; #6158 will
  restore the oauth-route freeze
- changelog: bullets for the above + the #6155 cooling-panel build fix

* chore(release): v3.8.44 — 2026-07-04

Release reconciliation + close (generate-release Phases 0a/1):
- CHANGELOG [3.8.44]: 21 PR refs added to existing bullets, 62 new bullets
  (incl. restoration of ~10 bullets erased by the stale-branch merge in
  1f6ec5bc8), 3 Maintenance rollups, #6061/#6130 credit fixes, 🙌 Contributors
  table (35 external contributors) — coverage 144/153 cycle commits by #ref
- 42 docs/i18n CHANGELOG mirrors synced (EN content; i18n workflow translates)
- README: What's New refreshed for v3.8.44 highlights
- build scope: exclude electron/node_modules + electron/dist-electron + .build
  from tsconfig (local build-output leak poisoned next build with 8GB OOM —
  same class as the 2026-06-25 incident; scope 14765→5207, gate green)
- quality: cyclomatic baseline 2026→2028 (+2 inherited end-of-cycle drift;
  verified the release-captain code fixes add 0 new violations)

* fix(release): v3.8.44 one-pass release-PR CI sweep

- fix(dashboard): /dashboard/system/proxy 500'd on EVERY render — #5918 put
  useProxyBatchOperations(load) before the const load declaration (TDZ
  ReferenceError, digest 539380095). Hook block moved after load; SSR
  renderToString regression test added (the exact crash mode).
- fix(server): TRACE/TRACK/CONNECT crashed Next's middleware adapter
  (undici cannot represent them) into a raw 500 on every route — the raw
  HTTP method guard now answers 405 + Allow up-front (dast-smoke
  Schemathesis finding on /api/keys/{id}/devices); guard test added.
- fix(api): restore Zod validation on the provider-scoped chat route via a
  .passthrough() schema preserving #5907's relaxed semantics (t06 gate).
- docs(openapi): /api/keys/{id}/devices 401 now refs the management error
  envelope (Schemathesis schema-conformance).
- quality: rebaseline i18nUiCoverage 77.5->76.8 (+~1352 new en.json UI keys
  from the cycle await the async translation workflow; v3.8.39 precedent).
- CodeQL: dismissed 2 incomplete-url-substring FPs on unit-test asserts
  (v3.8.35 precedent) with Hard Rule #14 justifications.
- changelog: bullets for the above + 42 i18n mirrors re-synced

* fix(release): round-2 CI findings — LocaleAutoDetect refresh gating + ratchet tighten

- fix(i18n): LocaleAutoDetect (#5979) refreshed the router on EVERY cookie-less
  first visit, even when the detected locale matched the server-rendered
  <html lang> — re-navigating mid-interaction (flaky e2e 'execution context
  destroyed' + visible flash for new visitors). Refresh now only fires when
  the locale actually differs; regression test added.
- quality: tighten openapiCoverage.pct 36.9->39.3 (require-tighten gate on the
  release PR; value measured by the CI Quality Ratchet on 00c55afcb)
- quality(file-size): shrink the ProxyRegistryManager TDZ note to fit the
  1117-line freeze (prettier reflow added a line at commit time)
- changelog bullet + 42 i18n mirrors re-synced

* test(release): collect the #6082 fingerprint-expansion ghost test

check:test-discovery (Lint job, layered behind the round-1 t06 fix) flagged
tests/e2e/fingerprint-expansion.test.ts as a NEW orphan — it is a node:test
server-boot test that no runner collected, so it had never run. Moved to
tests/integration/ (the collector for this shape), fixed the helper import,
and verified it actually passes (3/3 on first-ever run). CHANGELOG ref updated.

---------

Co-authored-by: Chirag Singhal <76880977+chirag127@users.noreply.github.com>
Co-authored-by: Hamsa_M <116961508+hamsa0x7@users.noreply.github.com>
Co-authored-by: Chewji <126886556+Chewji9875@users.noreply.github.com>
Co-authored-by: Vittor Guilherme Borges de Oliveira <vittoroliveira.dev@gmail.com>
Co-authored-by: nickwizard <35692452+nickwizard@users.noreply.github.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: Fadhil Yusuf <33994304+yusufrahadika@users.noreply.github.com>
Co-authored-by: Giorgos Giakoumettis <giorgos@yiakoumettis.gr>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: AgentKiller45 <jamalzzj45@gmail.com>
Co-authored-by: Nikolay Alafuzov <alafuzov_nn@rusklimat.ru>
Co-authored-by: ricatix <d.enistraju155@gmail.com>
Co-authored-by: Muhammad Mugni Hadi <mugni@rukita.co>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Ngô Tấn Tài <tantai@newnol.io.vn>
Co-authored-by: dopaemon <polarisdp@gmail.com>
Co-authored-by: yicone <yicone@gmail.com>
Co-authored-by: CườngNH <j2.cuong@gmail.com>
Co-authored-by: DuyPrX <93126969+DuyPrX@users.noreply.github.com>
Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
Co-authored-by: eng2007 <aleksey.semenov@gmail.com>
Co-authored-by: chamdanilukman <16629923+chamdanilukman@users.noreply.github.com>
Co-authored-by: Umar Javed <114807145+tn5052@users.noreply.github.com>
Co-authored-by: JiangZhuo <jiangzhuo@qiniu.com>
Co-authored-by: Delynn Assistant <zhen@dkzhen.org>
Co-authored-by: whale9820 <87256750+whale9820@users.noreply.github.com>
Co-authored-by: whale <admin@dyntech.cc>
Co-authored-by: Rigel Ramadhani Waloni <rigel8911@gmail.com>
Co-authored-by: zocomputer <help@zocomputer.com>
Co-authored-by: aristorinjuang <aristorinjuang@gmail.com>
Co-authored-by: anmingwei <anmingwei@dobest.com>
Co-authored-by: janeza2 <49841619+janeza2@users.noreply.github.com>
Co-authored-by: zmf963 <19422469+zmf963@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Koosha Pari <kooshapari@gmail.com>
Co-authored-by: hartmark <hartmark@users.noreply.github.com>
Co-authored-by: ron <devestacion@gmail.com>
Co-authored-by: Ansh7473 <Ansh7473@users.noreply.github.com>
Co-authored-by: felssxs <felssxs@users.noreply.github.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Semianchuk Vitalii <fix20152@gmail.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Milan Soni <123074437+Iammilansoni@users.noreply.github.com>
Co-authored-by: Devin <studyzy@gmail.com>
Co-authored-by: Raxxoor <manker_lol@hotmail.com>
Co-authored-by: derhornspieler <15236687+derhornspieler@users.noreply.github.com>
2026-07-04 13:00:30 -03:00

67 KiB
Raw Blame History

🚀 OmniRoute — 免费 AI 网关

🌐 语言: 🇺🇸 English · 🇸🇦 ar · 🇧🇬 bg · 🇧🇩 bn · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇪🇸 es · 🇮🇷 fa · 🇫🇮 fi · 🇫🇷 fr · 🇮🇳 gu · 🇮🇱 he · 🇮🇳 hi · 🇭🇺 hu · 🇮🇩 id · 🇮🇹 it · 🇯🇵 ja · 🇰🇷 ko · 🇮🇳 mr · 🇲🇾 ms · 🇳🇱 nl · 🇳🇴 no · 🇵🇭 phi · 🇵🇱 pl · 🇵🇹 pt · 🇧🇷 pt-BR · 🇷🇴 ro · 🇷🇺 ru · 🇸🇰 sk · 🇸🇪 sv · 🇰🇪 sw · 🇮🇳 ta · 🇮🇳 te · 🇹🇭 th · 🇹🇷 tr · 🇺🇦 uk-UA · 🇵🇰 ur · 🇻🇳 vi · 🇨🇳 zh-CN · 🇹🇼 zh-TW


OmniRoute 控制台

🚀 OmniRoute — 免费 AI 网关

编码,永无止境。通过一个端点,让所有 AI 工具直连 236 家服务商50+ 家免费

将 Claude Code、Codex、Cursor、Cline、Copilot 和 Antigravity 接入免费的 Claude / GPT / Gemini。自动容灾无感切换。


RTK + Caveman 压缩引擎Token 节省 1595%。从此告别用量限制。


约 1.6B 可统计免费 Token / 月 — 计入注册奖励后,首月最高可达 ~2.1B — 聚合各家免费层配额,外加一众永久免费、不限量的服务商;再叠加上述压缩引擎,每一枚 Token 都物超所值。(统计方法 →)


231 AI Providers 50+ Free 1.6B Free Tokens/mo Token Savings 17 Strategies $0 to start


💬 加入社区

Discord Telegram WhatsApp Global WhatsApp Brasil

疑难解答、服务商攻略、路线图与支持 → Discord · Telegram · WhatsApp 🌍 全球 / 🇧🇷 巴西


diegosouzapw%2FOmniRoute | Trendshift

npm License: MIT Node Stars

npm version NPM Monthly Docker Hub Docker Pulls Electron Downloads Website


🚀 快速开始🎯 Combo🌐 服务商🔌 CLI 与 MCP🗜️ 压缩🌍 官网

💥 我们的承诺🤔 为什么选择 OmniRoute🏆 核心优势🤖 兼容的编程工具🖥️ 运行平台🔒 隐私优先🎬 实机演示📚 探索更多📧 支持


💰 约 1.6B 免费 Token / 月

手动凑各家免费额度有多痛苦 — 数十套 SDK、数十个速率限制根本搞不清到底还剩多少。OmniRoute 将 40+ 服务商池 / 500+ 模型可核实免费层聚合为一个真实的统一数字,并在控制台实时展示 (/dashboard/free-tiers)。

  • 约 1.6B 免费 Token / 月(稳定值) — 注册奖励加持下,首月最高约 2.1B
  • 去重统计,诚实透明 — 每个共享免费池只计一次,标题数字不被速率上限注水。若以全天候速率上限累算会得出 ~10B 的虚假数据,我们从不发布此类数字。
  • 外加不可计数的部分 — 永久免费、无 Token 上限的服务商SiliconFlow、Z.AI GLM-Flash、Kilo、OpenCode Zen…以及 $10 的 OpenRouter 充值可解锁 +24M/月,二者独立列示,绝不混入标题数字。
  • 逐模型明细、当月已用 / 剩余实时显示,以及每家服务商的透明条款标注

Free-Tier Budget card (preview mockup)

示例预览 — 待 /dashboard/free-tiers 页面验证后替换为真实截图。完整统计方法(池去重、额度层级、服务商条款):docs/reference/FREE_TIERS.md


💥 我们的承诺

一个端点。236 家服务商。 编码不止步 — 让 OmniRoute 帮你选出最便宜且可用的那个。

🚫 永不触达限制
横跨 236 家服务商的毫秒级自动切换。配额耗尽?下一家即刻接管 — 零停机。
💸 Token 节省高达 95%
RTK + Caveman 级联压缩可削减 1595% 的可压缩 Token工具密集型会话平均约 89%)。
🆓 零元起步
50+ 家服务商提供免费层,其中 11 家永久免费Kiro、Qoder、Pollinations、LongCat…。无需绑卡。
🔌 所有工具一网打尽
16+ 款编程助手 — Claude Code、Codex、Cursor、Cline、Copilot、Antigravity — 一套配置全搞定。
🧩 一个端点通吃
OpenAI ↔ Claude ↔ Gemini ↔ Responses API 无缝翻译。任意工具指向 /v1 即开即用。
🛡️ 生产级品质
熔断器、TLS 指纹伪装、MCP87 工具、A2A、记忆系统、安全护栏、评估框架。14,965 项测试。


🤔 为什么选择 OmniRoute

告别在十个控制台之间疲于奔命、处理失效的 API 密钥和天降账单的日子。

日常痛点 OmniRoute 如何解决
📉 每月订阅配额用不完就浪费 压榨订阅价值 — 追踪配额,在重置前用尽每一枚 Token
🛑 写到一半被限速打断 四层自动切换 — 订阅 → API Key → 廉价 → 免费,毫秒级接续
🔥 工具输出(git diffgrep、日志)狂烧 Token RTK + Caveman 压缩 — 每次请求可省 1595% 可压缩 Token
💸 昂贵的 API每服务商 $2050/月) 成本优先路由 — 自动导向性价比最高的可用模型
🧰 每款 AI 工具各有一套繁琐配置 一个端点、一套配置、一个控制台
🌍 所在国家/地区封锁 AI 三级代理 + TLS 指纹伪装 — 无论身在何方AI 任你用
┌──────────────────────────────────────────────────────────┐
│        你的 IDE / CLI  (Claude Code, Cursor, Cline…)       │
└─────────────────────────┬──────────────────────────────────┘
                          │ http://localhost:20128/v1
                          ▼
┌──────────────────────────────────────────────────────────┐
│                  OmniRoute — 智能路由中枢                   │
│  RTK + Caveman 压缩 · 17 种路由策略                          │
│  熔断器 · TLS 指纹伪装 · MCP · A2A · 安全护栏                 │
└─────────────────────────┬──────────────────────────────────┘
        ┌─────────────┬────┴────────┬─────────────┐
        ▼ 第一梯队    ▼ 第二梯队    ▼ 第三梯队    ▼ 第四梯队
      订阅          API Key      廉价          免费
   Claude Code,     DeepSeek,      GLM $0.5,      Kiro, Qoder,
   Codex, Copilot   Groq, xAI      MiniMax $0.2   Pollinations
   配额耗尽? ───▶   预算触顶? ─▶  预算触顶? ─▶ 永久在线

🎯 Combo — 招牌功能

Combo 是 OmniRoute 自动路由的模型接力链路。配额耗尽、服务商宕机或成本飙升 — Combo 自动滑向下一个模型,无声无息。正是它让 OmniRoute 坚不可摧。 🛡️

零配置 — 只需设为 auto

无需预先配置 Combo。将模型 ID 设为 auto或其变体OmniRoute 会基于你已连接的服务商实时评分,自动构建虚拟 Combo

模型 ID 优化目标
auto 🎯 均衡默认LKGP — 沿用上次表现最好的服务商)
auto/coding 🧑‍💻 代码质量优先
auto/fast 最低延迟优先
auto/cheap 💰 单位 Token 成本最低优先
auto/offline 🔋 配额 / 限速余量最充裕优先
auto/smart 🔭 质量优先 + 10% 探索度以发现更优模型

🔀 或亲手定制 — 17 种路由策略

目标 对应策略 / 组合
🥇 榨干订阅额度再用付费 priority / fill-first
⚖️ 跨账号均衡负载 round-robin · weighted · p2c · least-used
💸 永远选最便宜的可行模型 cost-optimized · auto/cheap
🧠 模型间接力传递长上下文 context-relay · context-optimized
🎲 随机 / 隐私路由 random · strict-random
🧬 多模型并行 + 裁判裁决 fusion
📊 按剩余配额余量路由 reset-window · headroom
🤖 智能自动 auto9 维度评分)· lkgp · reset-aware

Auto-Combo 引擎基于 9 个维度(健康度、配额、成本、延迟、成功率、新鲜度…)逐候选打分 — 详见 docs/routing/AUTO-COMBO.md

🧱 内置三层容灾

层级 作用范围 机制
🔌 熔断器 整家服务商 停止向上游持续失败的服务商发送请求;自动探测恢复
💤 连接冷却 单个账号 / 密钥 跳过快触达速率上限的密钥,其余密钥继续服务
🎯 模型隔离 服务商 + 模型 仅隔离单一配额耗尽的模型,不影响该服务商的其他连接
Combo: "always-on"                         策略: priority
  1. cc/claude-opus-4-7   ← 订阅(先用满)
  2. cx/gpt-5.5           ← 第二订阅
  3. glm/glm-5.1          ← 廉价备选 ($0.5/1M)
  4. kr/claude-sonnet-4.5 ← 免费、无限(永不断线)
结论: 四层容灾 = 零停机

📖 Auto-Combo 引擎 · 容灾指南


🏆 OmniRoute 何以脱颖而出

功能 OmniRoute 其他路由方案
🌐 服务商数量 231 20100
🆓 免费服务商 50+ (其中 11 家永久免费) 15
🔀 路由策略 17 种(优先级、加权、成本优先、上下文中继、融合…) 13
🗜️ Token 压缩 RTK + Caveman 级联1595% 无 / 2040%
🧰 内置 MCP 服务器 87 个工具、3 种传输、30 个权限域 少见
🤝 A2A 代理协议 6 项技能、JSON-RPC 2.0
🧠 记忆系统FTS5 + 向量) 原生支持 少见
🛡️ 安全护栏PII、注入、视觉 原生支持 少见
☁️ 云代理 Codex、Devin、Jules
🥷 TLS 指纹伪装 JA3/JA4 基于 wreq-js
🖥️ 多平台 Web · 桌面 · Termux · PWA 仅 Web
🌍 国际化 42 种语言 04

📊 与 LiteLLM、OpenRouter、Portkey 的详细对比 → docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md


近期更新

v3.8.20 → v3.8.41 重点更新。完整日志见 CHANGELOG.md

  • ⚖️ Quota-Share 路由 — 专用 Combo 策略按可用配额跨账号分配负载Deficit-Round-Robin 调度、每连接 max_concurrent 配合冷却等待队列、多时间窗口用量桶5 小时 / 7 天 / 每模型)、每 (密钥, 模型) 用量上限、会话粘性保障 Prompt 缓存完整性,以及基于上游 Token 用量头的主动饱和检测。→ 容灾指南
  • 🤖 一键 CLI/Agent 配置 — 专用 setup-* 命令为各编程工具一键配置 OmniRoute 路由Claude Code、Codex、Cline、Continue、Cursor、Roo Code、Kilo Code、Crush、Goose、Qwen Code、Aider、OpenCodeomniroute launch / omniroute launch-codex 为零配置启动器。→ CLI 集成
  • 🛰️ 远程模式 — 通过授权范围 Token 从任意机器操控远程 OmniRouteomniroute connect / omniroute contexts / omniroute tokens);另附 omniroute login antigravity 辅助命令,在你的本机运行 Google "native/desktop" OAuth 后将凭证 blob 粘贴至远程/VPS 安装实例(因远程环境无法接收 loopback 回调)。→ 远程模式
  • 🧭 更智能的自动路由 — OpenRouter 风格的 auto/<category>:<tier> Comboauto/coding:fastauto/reasoning:pro)、Fusion 策略(并行分发至多模型面板后由裁判合成最优结果)、任务感知路由(按任务类型匹配最佳连接)、每请求 X-Route-Model 覆盖、实时 Arena-ELO + models.dev 模型智能评分、每步骤账号白名单、服务商通配符策略步骤、嵌套Combo引用执行、粘性加权选择以及 web_search 感知路由。→ Auto-Combo
  • 🗜️ 可插拔压缩体系9 大可组合引擎的异步流水线,含 Compression Studios、LLMLingua-2 ONNX 引擎和启发式/SLM 双层 Ultra、RTK、委托式 Anthropic 上下文编辑、输出风格(输出轴调控:简洁文章 / 少代码 / 简洁文言)、自适应上下文预算旋钮(仅推进到刚好适应上下文窗口的程度)、每请求 x-omniroute-compression 控制、可选离线评估套件、控制台一键 Headroom 代理生命周期管理(支持 Docker 边车)、合成压缩演练场Play 通道 + A/B 对比,附 USD 上限保真度判定)、可选每步保真度门控(在有损引擎降低 Prompt 质量前将其拦截)、Best-of-N 候选编码器GCF vs TOON — 取更短者Studio 中附 A/B 字节/Token 对照表)、CCR 范围/grep/统计检索(直接拉取储存块的精确字节/行切片或摘要而无需全量展开),以及统一面板含命名配置文件 + 活动配置文件选择器。→ 压缩
  • 🕵️ 透明 MITM 解密TPROXY — 捕获并翻译忽略代理环境变量的 CLI 流量,含每 SNI 证书颁发机构和信任存储安装器。→ MITM/TPROXY
  • 💸 全方位成本遥测 — 每个端点上的 X-OmniRoute-* 成本/用量响应头(含媒体端点)、非 Token 成本引擎、缓存命中 X-OmniRoute-Cost-Saved 响应头,以及每密钥美元消费配额。→ API 参考
  • 🧠 完全可控的记忆系统 — 可选 int8 向量量化Qdrant + sqlite-vec、默认关闭记忆、每请求 x-omniroute-no-memory 响应头。→ 记忆系统
  • 🛡️ 安全 — 所有 LLM 路由的提示注入防护(后台有红队测试套件),外加免费的 DuckDuckGo 兜底网页搜索。→ 安全护栏
  • 🤝 更多服务商与代理 — Cursor Cloud Agent第四云代理、CodeBuddy CNcopilot.tencent.com、Google Flow 视频生成服务商、新网关 DGridPioneer AIFastino Labs、入站 xAI Grok 翻译器加 Grok Build (xAI)(含 OAuth 导入 Token 流程、GitHub Copilot 服务商的 GPT-4 / GPT-4o-mini、多模型 Factory DroidZenMux Free(会话 Cookie 免费层)、阿里云 DashScope 文生视频(wan2.7-t2v)、刷新至 236 家服务商的目录OrcaRouter、Wafer AI、OpenAdapter、dit.ai、TokenRouter…、Vertex AI 媒体生成(语音/转录/音乐/视频),以及一键从 CLIProxyAPI 导入账号(~/.cli-proxy-api/)。→ 服务商
  • 本地性能与基础设施 — 一键本地 Redis 启动器(omniroute redis up,含控制台 Redis 面板)、一键 Cloudflare WorkersDeno Deploy 中继部署器(接入代理池),以及可选 Bifrost Go 边车将最热中继路径卸载至 Go 侧(BIFROST_BASE_URL,超时自动回退 TypeScript 路径)— 现支持中继后端选择器(OMNIROUTE_RELAY_BACKEND=ts|bifrost|auto/v1/relay 端点保持对外稳定接口的同时内部自动择取最快后端。→ 环境配置

🤖 兼容的 CLI 与编程助手

一个配置 — http://localhost:20128/v1 — 所有 AI IDE 或 CLI 都能跑在免费与低成本模型上。

Claude Code
Claude Code
Codex CLI
Codex CLI
Cursor
Cursor
Copilot
Copilot
Continue
Continue
OpenCode
OpenCode
Kilo Code
Kilo Code
Droid
Droid
OpenClaw
OpenClaw
Kiro
Kiro
Command Code
Command
同样兼容 · Cline · Antigravity · Windsurf · AMP · Hermes · Qwen CLI · Roo · Continue · 任意兼容 OpenAI 格式的工具

📖 16+ 款工具的逐项配置指南 → docs/reference/CLI-TOOLS.md · 🧩 OpenCode 插件 → @omniroute/opencode-provider


🌐 231 家 AI 服务商 — 50+ 家免费

开源路由方案中最完整的服务商目录:236 家服务商50+ 家含免费层11 家永久免费

🆓 永久免费 — 零元,无需绑卡

AgentRouter
GPT-5、Claude、Gemini
$100 免费额度
Qoder AI
Kimi-K2、DeepSeek-R1
无限免费
Pollinations
GPT-5、Claude、Llama 4
无需密钥
LongCat
LongCat-2.0
一次性 10M Token (需 KYC) 🔑
Cloudflare AI
50+ 模型
10K 神经元/天
NVIDIA NIM
129 个模型
~40 RPM 免费
Cerebras
Qwen3 235B
1M Token/天

📖 完整机器可读目录 → docs/reference/PROVIDER_REFERENCE.md


🖥️ OmniRoute 运行平台 — 无处不在

同一套应用,你的机器,你的规则。从全局 npm install你的手机(通过 Termux无所不跑。

平台 安装方式 亮点
📦 npm全局 npm install -g omniroute 一行命令,任意 OS
🐳 Docker docker run … diegosouzapw/omniroute 多架构 AMD64 + ARM64
🖥️ 桌面Electron npm run electron:build 原生窗口 + 系统托盘 — Windows / macOS / Linux
💪 ARM 原生 arm64 树莓派、ARM 服务器、Apple Silicon
📱 AndroidTermux pkg install nodejs && npx -y omniroute 在手机上 7×24 运行,无需 Root
📲 PWA "添加到主屏幕" 全屏、离线、可从浏览器安装
🧩 OpenCode 插件 @omniroute/opencode-provider 原生 OpenCode 集成
🛠️ 源码构建 npm install && npm run dev 动手改造,贡献代码

📖 Docker 指南 · 桌面端 · Termux · PWA · OpenCode


🔒 隐私优先,数据本地

你的密钥、你的机器、你的数据。OmniRoute 是本地代理 — 绝不会向外回传。

  • 🏠 100% 运行在本地硬件上 — npm、Docker、桌面端或你的手机。请求链路中不存在任何 OmniRoute 云端节点。
  • 🔐 凭据静态加密 — API 密钥与 OAuth 令牌以 AES-256-GCM 封存。
  • 🚫 默认零遥测 — 你的提示只发送给你选定的服务商,别无他处。
  • 🛡️ 网关加固 — API 密钥权限域、IP 过滤、速率限制、提示注入防护、仅限 loopback 的进程路由。
  • 📜 MIT 协议、完全开源 — 逐行可审计,永久可自托管。

📖 授权管理 · 安全护栏 · 合规体系


🔌 完整 CLI + A2A 与 MCP

OmniRoute 不只是一台服务器 — 它是拥有 60+ 命令全功能命令行驾驶舱,外加开放的代理协议,让 AI 代理自主操控 OmniRoute。

⌨️ 真正的 CLI不止 start

omniroute               # 启动网关 + 控制台(端口 20128
omniroute chat          # 交互式 TUI 聊天客户端(斜杠命令:/model /combo /skill /memory
omniroute setup         # 引导式首次设置向导
omniroute doctor        # 诊断服务商、端口、原生依赖

🛰️ 远程模式 — CLI 在本地OmniRoute 在远端的 VPS

OmniRoute 跑在服务器上?用同一套 CLI 从笔记本远程操控。登录一次,绑定授权范围 Token后续所有命令自动指向远端。

omniroute connect 192.168.0.15            # 密码 → 范围 Token保存为上下文
omniroute models list                     # ← 在远端服务器上执行
omniroute configure codex                 # ← 选择远端模型,写入本地 Codex 配置文件
omniroute tokens create --name ci --scope read   # 为其他机器签发更窄范围的 Token
omniroute contexts use default            # ← 切回本机服务器

Token 权限域为 read / write / admin;涉及进程启动的路由仅限 loopback 执行。 📖 远程模式

providers · oauth · keys · combo · nodes · models · cache · compression · cost · usage · quota · health · resilience · telemetry · logs · audit · mcp · a2a · cloud · memory · skills · eval · tunnel · backup · sync · webhooks · policy · pricing · translator · simulate

🤝 接入 AI 代理 — 让代理自主操控 OmniRoute

通过 MCPA2A 协议暴露 OmniRoute任何智能代理都能获得网关的完整控制权 — 路由、服务商、Combo、缓存、压缩、记忆 — 全自主运行。

协议 端点 用途
🧰 MCPstdio omniroute --mcp 接入 Claude Desktop、Cursor 等各种 MCP 客户端
🌊 MCPHTTP http://localhost:20128/api/mcp/stream 远程 MCP — 87 个工具、30 个权限域、完整审计追踪
📡 MCPSSE http://localhost:20128/api/mcp/sse 流式 MCP 传输
🤝 A2A http://localhost:20128/.well-known/agent.json 代理间通信,JSON-RPC 2.0 + SSE6 项技能
# 通过 MCP 将 OmniRoute 完整工具集赋予 Claude Code
claude mcp add-server omniroute --type http --url http://localhost:20128/api/mcp/stream

📖 MCP 服务器 · A2A 服务器 · 代理协议


🗜️ 自动节省 1595% Token

Token 够用就好,何必铺张浪费? 每个请求透明地通过 OmniRoute 压缩流水线 — 客户端无需任何改动。现已升级为 9 大可组合引擎的级联体系,按 Combo 自由排列组合 — 凝聚了 RTKCaveman 51K+)、LLMLingua-2TrogloditaPT-BR的技术精华。

🧱 九引擎级联体系

引擎按流水线顺序执行;每个引擎均可独立启停,按 Combo 粒度配置:

# 引擎 作用
1 Session-Dedup 剔除跨轮次重复的内容(基于内容寻址,跨轮次比对)
2 CCR 将大文本块归档到检索标记后,按需拉取
3 RTK 智能工具输出过滤、去重与截断(理解命令语义)
4 Headroom 同构 JSON 数组的无损表格式压缩(~30%+
5 Caveman 基于规则的叙述性文本压缩(输出端约 6575%
6 LLMLingua-2 基于 MobileBERT ONNX 的 ML 语义剪枝 — 代码安全、异步
7 Lite 空白符 + 图片 URL 精简(低延迟基线)
8 Aggressive 摘要浓缩 + 老旧轮次渐进式老化
9 Ultra 启发式 Token 剪枝 + 可选小模型SLM

代码块、URL 和结构化数据永远逐字节原样保留一键预设快速组合引擎:

模式 节省比例 最佳场景
🪶 Lite ~15% 常驻开启的安全默认
🪨 标准Caveman ~30% 日常编码
Aggressive ~50% 长时间工具密集型会话
🔥 Ultra ~75% 最大化节省
🧰 RTK 6090% Shell/测试/构建/Git 输出
🔗 级联RTK → Caveman 7895% 混合提示 + 工具日志

真实案例 — 标准模式:

压缩前69 Token "The reason your React component is re-rendering is likely because you're creating a new object reference on each render cycle. When you pass an inline object as a prop, React's shallow comparison sees it as a different object every time, which triggers a re-render. I would recommend using useMemo to memoize the object."

压缩后19 Token "New object ref each render. Inline object prop = new ref = re-render. Wrap in useMemo."

同样的回答。节省 72% Token。精度毫无损失。

PT-BR 案例 — Troglodita 模式:

压缩前42 Token "O problema é que o componente está re-renderizando porque uma nova referência de objeto está sendo criada em cada ciclo de renderização. Eu recomendaria usar useMemo."

压缩后12 Token "Re-render: ref nova cada ciclo (objeto inline recriado). Usar useMemo."

同样的回答。约 70% 更少 Token。技术精度完好无损。


📖 工作原理 — 流水线、架构与节省量计算

Client (10,000 tok) ──▶ OmniRoute Compression (9 engines) ──▶ Provider (~1,080 tok, 节省高达 95%)

默认级联组合为 RTK → Caveman。当二者作用于同一工具/上下文负载时,节省效果叠加:

组合节省率 = 1  (1  RTK) × (1  Caveman_input)
平均值     = 1  (1  0.80) × (1  0.46) = 89.2%
区间       = 78.4  94.6%

代码块、URL、JSON 和结构化数据始终受到保护引擎的保全。

🎚️ 引擎之外 — 输出风格、自适应旋钮与逐请求控制

上述 9 大引擎负责压缩输入端。还有三个额外层面,分别控制如何压何时压以及输出端的效果

  • 🪄 输出风格 (输出轴调控) — 注入确定性强、缓存友好的响应结构指令;可组合使用,每项提供 lite / full / ultra 三个强度档。添加风格只需一行注册代码:
    • 简明文章 — 剔除填充词/冠词/暧昧语;技术实质精确传达。
    • 少即是多 — "经验丰富的高级开发" YAGNI 风格:最小化可用改动,不主动添加脚手架。
    • 文言简雅 — 仿文言文的极致简洁风格(区域锁定至 zh)。
  • 🎯 自适应上下文预算 (调节旋钮) — 取代简单的开/关阈值,改为渐次递进:从最轻量、最无损的引擎开始,仅推进到刚好适配目标模型上下文窗口的程度。策略:reserve-output(默认,模型感知)· percentage · absolute。模式:floor(确保适配)· replace-autotrigger(你的显式选择优先)· off(传统阈值模式)。
  • 🎛️ 压缩决策的优先链路 (从高到低) — 逐请求 x-omniroute-compression Combo 覆写 活动命名配置 自适应/自动触发 面板默认 关闭。最终采用的压缩方案会通过 X-OmniRoute-Compression: <mode>; source=<source> 响应头回显。

可依阈值自动触发、旋钮自适应调节、固定命名配置文件、逐请求一次性压缩或为每条Combo 专属分配流水线 — 工作负载千差万别,总有一种适配。可选离线评估套件npm run eval:compression)在固定语料集上量化评分,助你在推广变更前验证保真度与节省效果。

📖 COMPRESSION_GUIDE.md · RTK_COMPRESSION.md · COMPRESSION_ENGINES.md


快速开始

1) 安装并运行

npm install -g omniroute
omniroute

控制台:http://localhost:20128 · APIhttp://localhost:20128/v1

2) 连接免费服务商(无需注册)

控制台 → Providers → 连接 Kiro AI(免费 Claude约 50 积分/月/账号)或 OpenCode Free(无需认证)→ 完成。

3) 配置你的编程工具

Base URL: http://localhost:20128/v1
API Key:  [从 控制台 → Endpoints 复制]
Model:    auto            (零配置智能路由 — 也可指定任意服务商/模型)

4) 验证链路

curl http://localhost:20128/v1/models -H "Authorization: Bearer YOUR_KEY"

你应该能看到已连接模型的列表。🎉 至此大功告成 — 开始编码OmniRoute 自动路由、自动容灾。

如果你的客户端无法发送自定义请求头OmniRoute 也提供 Token 化兼容别名:

OpenAI 模型目录:  http://localhost:20128/vscode/YOUR_KEY/
OpenAI 模型列表:  http://localhost:20128/vscode/YOUR_KEY/models
OpenAI 聊天:      http://localhost:20128/vscode/YOUR_KEY/chat/completions
OpenAI 响应:      http://localhost:20128/vscode/YOUR_KEY/responses
Ollama 聊天:      http://localhost:20128/vscode/YOUR_KEY/api/chat
Ollama 标签:      http://localhost:20128/vscode/YOUR_KEY/api/tags

仅限无法附带 Authorization: Bearer ... 头的客户端使用。标准请求头认证始终是推荐方式。


📦 更多安装方式 — Docker、源码、pnpm、Arch

🐳 Docker

docker run -d --name omniroute --restart unless-stopped --stop-timeout 40 \
  -p 20128:20128 -v omniroute-data:/app/data diegosouzapw/omniroute:latest

🛠️ 源码构建

cp .env.example .env && npm install
PORT=20128 npm run dev

📦 pnpm

pnpm add -g omniroute@latest --allow-build=better-sqlite3 --allow-build=@swc/core && omniroute

🐧 Arch LinuxAUR

yay -S omniroute-bin && systemctl --user enable --now omniroute.service

🔧 NixFlake

# 使用 Nix flakes
nix develop
npm run dev

# 或使用 devbox
devbox run npm run dev

📖 Docker 指南 — Compose 配置、Caddy HTTPS、Cloudflare 隧道。

🦭 Podman

# 1. 构建镜像
podman build --target runner-base -t omniroute:base .

# 2. 修复无 Root 权限 Podman 的数据目录权限
mkdir -p data && podman unshare chown 1000:1000 ./data

# 3. 在 .env 中设置运行时,然后运行(参见 contrib/podman/ 中的 Quadlet
echo "CONTAINER_HOST=podman" >> .env
podman compose --profile base up -d

📖 Podman 指南 — Quadlet 设置、podman-compose、Quadlet。


🎬 实机演示

Guia em Português
🇧🇷 Português
Guia completo
English Guide
🇺🇸 English
Complete walkthrough
Руководство
🇷🇺 Русский
Полное руководство

🎬 制作了关于 OmniRoute 的视频? 通过链接创建 issuediscussion — 我们将在本节予以展示。


📚 探索更多

💰 费用一览与零元免费栈11 家服务商)
层次 举例 成本
💳 订阅制 Claude Code Pro / Codex / Copilot $10200/月
🔑 API Key含免费层 NVIDIA NIM、Cerebras、Groq 免费
💰 廉价 GLM-5 $0.5/1M · MiniMax M2.5 $0.3/1M 几分钱
🆓 永久免费 Kiro、Qoder、Qwen、Pollinations、LongCat $0

零元免费栈 — 合并为一条坚不可摧的 Combo

服务商 前缀 免费模型 配额
Kiro kr/ Claude Sonnet 4.5、Haiku 4.5、Opus 4.6 50 积分/月
Qoder if/ kimi-k2-thinking、qwen3-coder-plus、deepseek-r1 ♾️ 无限
Qwen qw/ qwen3-coder-plus/flash/next ♾️ 无限
Pollinations pol/ GPT-5、Claude、Gemini、DeepSeek、Llama 4 无需密钥
LongCat lc/ LongCat-2.0 一次性 10M (需 KYC)
Cloudflare AI cf/ 50+ 模型 10K 神经元/天
NVIDIA NIM nvidia/ 129 个模型 ~40 RPM
Cerebras cerebras/ Qwen3 235B、GPT-OSS 120B 1M Token/天

💡 控制台上的"费用"是节省追踪器,而非账单 — OmniRoute 从不向你收费。显示"$290 总费用"意味着你使用免费模型省下了 $290

📖 完整免费服务商目录 → docs/reference/FREE_TIERS.md — 25+ 家服务商、配额、Base URL。

🎯 实用场景 — 即拿即用的 Combo 配方

永久零元:

1. kr/claude-sonnet-4.5   (Kiro — ~50 积分/月/账号)
2. if/kimi-k2-thinking    (Qoder — 无限)
3. pol/gpt-5              (Pollinations — 无需密钥)
4. lc/LongCat-2.0         (一次性 10M 备用,需 KYC)
压缩方案: aggressive (~50%) → 免费额度翻倍 · 成本: $0/月

7×24 无中断: 串联 2 个订阅 → 廉价 → 免费,五层容灾。 地理封锁区: 免费服务商 + 全局/按服务商代理 → 从任何国家访问 AI。 最大化节省: 订阅 + 廉价备用 + ultra 压缩(~75%)→ 重度用户每月节省约 $150300。

🌍 绕过地理封锁 — 三级代理 + 隐身

🇷🇺 🇨🇳 🇮🇷 🇨🇺 🇹🇷 身处受限地区OmniRoute 的三级代理体系(全局 / 按服务商 / 按连接)代理 API 请求、OAuth 流程、连通性测试、Token 刷新和模型同步。

  • 协议: HTTP/HTTPS、SOCKS5、需认证代理
  • 🆓 1proxy 市场 — 数百个免费验证代理、质量评分、自动轮换
  • 反检测 — TLS 指纹伪装(wreq-js、CLI 指纹匹配、代理 IP 保持

📖 docs/ops/PROXY_GUIDE.md

完整功能清单 — 30+ 核心能力(记忆、评估、可观测性)

路由: 15 种策略 · 任务感知智能路由 · 思考预算控制 · 通配符路由 · 系统提示注入。 兼容性: OpenAI ↔ Claude ↔ Gemini ↔ Responses API · 自动 OAuth 刷新PKCE8 家服务商)· 多账号轮询 · Batch + Files API · 实时 OpenAPI 3.0。 协议: MCP87 工具、3 种传输、30 个权限域)· A2AJSON-RPC 2.0、SSE、6 项技能)· ACP · 云代理Codex、Devin、Jules插件: 自定义插件市场(系统配置的注册 URL带 SSRF 防护拉取)· 安装/启用/禁用 · Notion + Obsidian 知识库集成WebDAV 文件服务器、仓库搜索、笔记 CRUD嵌入式服务: 一键安装与生命周期管理本地边车服务CLIProxy、NineRouter质量与运维: 内置 Evals 评估框架(黄金标准集:精确匹配/包含/正则/自定义)· 安全护栏PII 脱敏、注入防护、视觉桥接)· 健康监控面板 · p50/p95/p99 遥测 · Webhooks · 合规审计。 AI Agent 技能: 即插即用的 Markdown 技能清单 — 将任意代理指向 skills/*/SKILL.md 清单。43 项可用技能。

📖 MCP 服务器 · A2A 服务器 · 容灾指南 · 功能画廊

📖 环境变量、设置与常见问题
环境变量 默认值 用途
PORT 20128 API + 控制台端口
REQUIRE_API_KEY false 是否要求所有请求携带 API Key
DATA_DIR ~/.omniroute 数据库与配置存储路径

OmniRoute 会向我收费吗? 不会 — 它是运行在你本机的免费开源软件。你只直接向付费服务商付款。OmniRoute 不含任何计费系统。 免费服务商真的无限使用吗? 绝大多数是 — Qoder、Pollinations、LongCat 和 Cloudflare 免费且无单账号额度上限。Kiro 也是免费,但每月每账号约 50 积分封顶。在 Combo 中叠加多家免费服务商,自动容灾确保零元持续可用。 压缩会影响输出质量吗? 不会 — 它仅压缩输入代码、URL、JSON 永远保留不损。 AI 服务被封锁的地区能用吗? 能 — 三级代理 + 1proxy 市场可覆盖全部 236 家服务商。

📖 用户指南 · API 参考 · 环境配置

🐛 故障排除
问题 快速解决方案
"Language model did not provide messages" 服务商配额耗尽 → 使用 Combo 自动切换
速率限制429 设置容灾链路:cc/claude → glm/glm-4.7 → if/kimi-k2-thinking
OAuth Token 过期 自动刷新;若卡住,在 Providers 页面删除后重新认证
unsupported_country_region_territory 在设置 → 代理中配置代理
Docker SQLite 锁定 使用 --stop-timeout 40 确保干净的 WAL 检查点
Node 运行时错误 使用 Node >=22.0.0 <23>=24.0.0 <27

🐛 报告 Bug 运行 npm run system-info 并附上生成的 system-info.txt📖 docs/guides/TROUBLESHOOTING.md

📸 控制台截图
页面 截图 页面 截图
Providers Providers Combos Combos
Analytics Analytics Health Health
Translator Translator Settings Settings
CLI Tools CLI Tools Usage Logs Usage

📧 支持与社区

💬 与社区交流 — Discord、Telegram 和 WhatsApp🌍 / 🇧🇷)链接详见 本 README 顶部



🛠️ 技术栈

  • 运行时Node.js 22.x 或 24.x LTS推荐 24 LTS>=22.0.0 <23 || >=24.0.0 <27
  • 语言TypeScript 6.0 — 跨 src/open-sse/ 100% TypeScript(核心模块自 v2.0 起零 any
  • 框架Next.js 16 + React 19 + Tailwind CSS 4
  • 数据库better-sqlite3 (SQLite) + LowDBJSON 兼容)— 域状态、代理日志、MCP 审计、路由决策、记忆、技能
  • 模式校验ZodMCP 工具 I/O 校验、API 合约)
  • 协议MCPstdio/HTTP+ A2A v0.3JSON-RPC 2.0 + SSE
  • 流式传输服务器推送事件SSE+ WebSocket 桥接(/v1/ws
  • 认证OAuth 2.0PKCE+ JWT + API Key + MCP 权限域授权
  • 测试Node.js 原生测试运行器 + Vitest14,965 个测试用例,覆盖 517 个文件 — 单元、集成、E2E、安全、生态
  • 平台桌面端Electron、AndroidTermux、PWA任意浏览器
  • CI/CDGitHub ActionsRelease 时自动发布至 npm + Docker Hub
  • 官网omniroute.online
  • npm 包npmjs.com/package/omniroute
  • Dockerhub.docker.com/r/diegosouzapw/omniroute
  • 容灾熔断器、指数退避、防惊群效应、TLS 伪装、Auto-Combo 自愈

📖 文档

📘 入门指南

文档 说明
用户指南 服务商、Combo、CLI 集成、部署
设置指南 全安装方法、CLI 工具配置、协议设置、超时调优
CLI 工具指南 Claude Code、Codex、Cursor、Cline、OpenClaw、Kilo、Copilot 逐工具配置
远程模式 通过授权范围 Token 从笔记本 CLI 操控远端 OmniRouteVPS
Claude Code 配置 使用 launch + 按模型配置文件将 Claude Code 指向 OmniRoute本地/远程)
快速开始 三步搞定:安装 → 连接 → 配置

🔧 运维与部署

文档 说明
Docker 指南 Docker 运行、Compose 配置、Caddy HTTPS、隧道、镜像标签
Podman 指南 Quadlet systemd 集成、podman-compose、SELinux
虚拟机部署 完整指南VM + nginx + Cloudflare 配置
Fly.io 部署 部署至 Fly.io含持久化存储
Termux 指南 通过 Termux 在 Android 上运行 OmniRoute
PWA 指南 渐进式 Web 应用安装、缓存、架构
卸载指南 所有安装方式的干净移除
环境配置 完整 .env 变量与参考

🧠 功能与架构

文档 说明
架构 系统架构、数据流与内部机制
压缩指南 七级选项流水线off / lite / standard / aggressive / ultra / RTK / stacked
RTK 压缩 命令输出压缩、过滤器、信任、验证、原始输出恢复
压缩引擎 Caveman、RTK、级联流水线、控制台/API/MCP 操作界面
压缩规则格式 Caveman 和 RTK 过滤器的 JSON 规则包 Schema
压缩语言包 语言检测与 Caveman 规则包编写
容灾指南 熔断器、冷却、队列、防惊群效应、TLS 伪装
Auto-Combo 引擎 九维度评分、模式包、自愈
代理指南 三级代理体系、1proxy 市场、注册 CRUD
免费服务商 25+ 家免费 API 服务商统一目录
功能画廊 带截图的控制台视觉导览
代码库文档 新手友好的代码库导览

🤖 协议与 API

文档 说明
API 参考 全端点含示例
OpenAPI 规范 OpenAPI 3.0 规格
MCP 服务器 87 个 MCP 工具、IDE 配置、Python/TS/Go 客户端
MCP 服务器指南 MCP 安装、传输与工具参考
A2A 服务器 JSON-RPC 2.0 协议、技能、流式传输、任务管理
A2A 服务器指南 A2A Agent Card、任务、技能与流式传输

📋 项目与质量

文档 说明
贡献指南 开发环境设置与规范
更新日志 完整按版本发布历史
安全策略 漏洞报告与安全实践
i18n 指南 40+ 语言支持、翻译流程、RTL
发布检查清单 发布前验证步骤
测试覆盖计划 测试覆盖策略与 14,965 测试套件

核心贡献者

OmniRoute 由充满热情的开源社区共同塑造。以下同仁做出了卓越贡献,直接影响着项目的质量、稳定性与影响力。衷心感谢。

oyi77
oyi77

🥇 190 次提交 · +72K 行
分析引擎、SQL 聚合、
代理市场、测试覆盖
Chris Staley
Chris Staley

🥈 72 次提交 · +5.7K 行
SSE 流加固、Responses API、
Gemini 分页、回归修复
zenobit
zenobit

🥉 62 次提交 · +24K 行
CI/CD 流水线、33 种语言 i18n、
Void Linux 包、跨平台修复
R.D. & Randi
R.D. & Randi

🏅 107 次提交 · +28K 行
Endpoints 页面、隧道集成、
Docker 工作流、A2A 状态、压缩 UI
benzntech
benzntech

🏅 20 次提交 · +7.5K 行
Electron 桌面应用、自动更新、
发布构建工作流、跨平台 CI

🙏 这些贡献者的功能、Bug 修复和基础设施改进,是 OmniRoute 可靠且功能丰富的核心支柱。每一个 Pull Request、每一个测试用例、每一个 i18n 翻译文件都意义重大。开源正是由他们这样的人建造的。



👥 贡献者

Contributors

如何贡献

  1. Fork 本仓库
  2. 创建功能分支(git checkout -b feature/amazing-feature
  3. 提交更改(git commit -m 'Add amazing feature'
  4. 推送分支(git push origin feature/amazing-feature
  5. 创建 Pull Request

详见 CONTRIBUTING.md 获取完整开发指南。

发布新版本

# 创建 Release — npm 发布将自动触发
gh release create v3.8.2 --title "v3.8.2" --generate-notes

📊 Star 历史

Star History Chart

🌍 StarMapper

StarMapper

🙏 致谢

OmniRoute 是站在巨人肩膀上的作品。它始于 9router 的一个 Fork 以及 Go 项目 CLIProxyAPI 的 TypeScript 移植 — 自此,以下每个子系统均受惠于先行者的开源成果。每一个项目都在 OmniRoute 中留下了具体印记。这是我们对所有项目的由衷感谢。🙏

星标数为 2026 年 6 月数据 — 请给这些项目点颗星。

🧬 渊源与网关

项目 对 OmniRoute 的启发
9router · decolua 17.9k 此 Fork 所基于的原型项目 — 此处扩展了多模态 API 并完成了全面 TypeScript 重写。
CLIProxyAPI · router-for-me 37.8k 启发本 JavaScript/TypeScript 移植版的 Go 语言实现。
LiteLLM · BerriAI 50.8k AI 网关,其公开定价数据集为我们提供成本同步数据,其服务商规范化模型启发了我们的路由体系。

🗜️ 上下文与 Token 压缩 — 引擎

项目 对 OmniRoute 的启发
Caveman · JuliusBrussee 74.5k "Token 够用就好"爆款项目 — 其原始人风格哲学驱动着我们的标准压缩模式及 30+ 条填充词/凝练规则。
RTK Rust Token Killer · rtk-ai 63.6k 高性能命令输出压缩 — 启发了我们的 RTK 引擎、JSON 过滤器 DSL、原始输出恢复及 RTK → Caveman 级联流水线。
headroom · chopratejas 33.6k 可逆上下文压缩SmartCrusher— 启发了我们的 headroom 引擎及 ccr 检索标记模式。
LLMLingua · Microsoft 6.3k 提示压缩研究LLMLingua / LLMLingua-2— 启发了我们的异步、代码安全、Fail-Open 的 llmlingua 引擎。
llmlingua-2-js · atjsh 27 JS/ONNX 移植MobileBERT / XLM-RoBERTa用作我们 LLMLingua 引擎的 Worker Thread 后端。
Troglodita · Lenine Júnior 15 PT-BR Token 压缩 — 驱动我们的 pt-BR 语言包:针对巴西葡萄牙语语法调优的赘语消减与填充词移除。
ponytail · DietrichGebert 51.4k "经验丰富的高级开发" YAGNI 编码技能 — 启发了我们的少即是多输出风格:最小化可用改动引导,减少生成代码量。

🧩 紧凑格式、Token 研究与代码感知工具

项目 对 OmniRoute 的启发
TOON · toon-format 24.6k Token 导向对象表示法 — 其列式、表头加行的数据模型塑造了我们的表格式压缩阶段。
GCF Graph Compact Format · Blackwell Systems 11 模式感知的"LLM 专用 JSON"表示法 — 共同启发了我们带 [N rows] 标记的无损同构数组压缩。
token-optimizer-mcp · ooples 409 Brotli/SQLite 缓存 + 按会话上下文增量 — 启发了我们的 session-dedup 引擎。
token-savior · Mibayy 993 Bash 输出压缩 + MCP 配置文件 — 启发了我们的压缩安全回退机制及 MCP 工具清单简化。
token-saver · ppgranger 103 内容感知、按文件类型输出压缩及故障感知回退 — 验证了我们的按类型分发和最低收益跳过策略。
token-optimizer · alexgreensh 1.4k "发现隐藏 Token" — 其卸载+可恢复句柄模式启发了我们的 CCR 卸载思路。
TokenMizer · Shweta-Mishra-ai 1 会话图 + 跨轮次行去重蓝图,启发了我们的 session-dedup 设计。
OmniCompress · jessefreitas 2 Rust 列式 JSON + 内容寻址检索 + 跨消息去重 — 验证了我们 headroom/ccr/session-dedup 引擎设计及"压缩形态位置无关"的缓存稳定不变量。
mcp-compressor · Atlassian Labs 80 MCP 工具 Schema/描述压缩 — 启发了我们的 MCP 工具清单基数缩减。
RepoMapper · pdavis68 182 Aider 风格仓库地图排序 — 启发了我们的仓库地图/检索排序探索。
quiet-shell-mcp · mrsimpson 4 基于 MCP 的声明式 Shell 输出缩减 — 验证了我们的声明式 Bash 输出压缩。
ts-morph · David Sherret 6.1k TypeScript 编译器 API 工具包 — 启发了我们基于解析器的注释移除,完整保留字符串、模板和正则字面量。

🧠 记忆与 RAG

项目 对 OmniRoute 的启发
Mem0 · mem0ai 58.9k 通用记忆层 — 其代理即写入/读取边界模型塑造了我们的记忆架构。
Letta (MemGPT) · letta-ai 23.4k 具备分层记忆的有状态代理 — 启发了我们的上下文控制与恢复CCR分层模型。
WFGY · onestardao 1.8k 16 种常见 RAG/LLM 失效模式的 ProblemMap 分类法 — 构成了我们故障排除指南的共享词汇。

🛰️ 流量检查、MITM 与透明代理

项目 对 OmniRoute 的启发
llm-interceptor · chouzz 46 编码助手 ↔ LLM 流量 MITM 拦截/分析 — 我们的流量检查器移植了其 SSE 合并、对话归一化、主机透传及密钥掩码方案。
ProxyBridge · InterceptSuite 5.1k 透明每进程代理路由 — 启发了我们崩溃安全的 MITM 拆卸、Socket 空闲超时、/proc 进程归因及 TPROXY 捕获。

📚 模型数据、可观测性与 UI

项目 对 OmniRoute 的启发
models.dev · SST / OpenCode 5.1k AI 模型规格、定价与能力的开放数据库 — 原生同步至我们的模型目录。
React Flow / xyflow · xyflow 37.1k 驱动我们实时 Compression Studio 及 Combo/Routing Studio 的基于节点的图形库。
LangGraph · LangChain 35.1k LangGraph Studio 的实时工作流图形可视化启发了我们 Studios 的实时级联视图。
Langfuse · Langfuse 29.3k 其 trace → span → generation 可观测性模型塑造了我们的 Compression Studio 瀑布图。
Kiali · Kiali 3.6k Istio 服务网格可观测性 — 启发了我们 Routing/Combo Studio 中的熔断器徽章和错误边界可视化。
lobe-icons · LobeHub 2.1k AI/LLM 品牌图标,渲染控制台中各服务商标识。

🛡️ 安全

项目 对 OmniRoute 的启发
awesome-secure-defaults · tldrsec 708 一份精选的安全默认库清单指导我们的安全技术选型Helmet.js、DOMPurify、ssrf-req-filter、safe-regex、Google Tink

❤️ 支持

OmniRoute 是免费开源项目,在公开环境中持续构建与维护。如果它帮你节省了时间或金钱,请考虑以以下方式支持开发:

  • 为本仓库加颗 Star — 这确确实实能帮我们提升可见度
  • 💖 GitHub Sponsors — 资助持续维护和新服务商接入
  • 🐛 Discussions 中反馈 Bug 和分享意见

📄 许可证

MIT 协议 — 详见 LICENSE


⬆ 返回顶部 · 用 ❤️ 为开源 AI 社区构建。

OmniRoute v3.8.24 · Node ≥22.0.0 · MIT License · omniroute.online