* feat(sse): adapt the Claude flow to Opus 4.7+ adaptive-thinking + fixed-sampling contract
Claude Opus 4.7 and later (Opus 4.7/4.8, Fable 5) changed the Messages API contract:
manual extended thinking was removed (`thinking.type:"enabled"` or ANY `budget_tokens`
returns 400 — adaptive-only, steered by `output_config.effort`) and non-default
`temperature`/`top_p`/`top_k` now return 400 (sampling is fixed). OmniRoute still emitted
both shapes, so OpenAI-format `reasoning_effort` low/medium/high and any client-supplied
sampling param could hard-400 on the most-used provider.
- modelSpecs: new `adaptiveThinkingOnly` flag + `isAdaptiveThinkingOnly()` on opus-4-7/4-8/fable-5
- translator: `reasoning_effort` (every level) -> adaptive + `output_config.effort` for those models
- chatCore: `normalizeClaudeAdaptiveThinking` catch-all collapses residual manual thinking
(passthrough legacy shape / per-model defaults) to `{type:"adaptive"}`, keyed on the model
- registry: strip `temperature`/`top_p`/`top_k` for opus-4-7/4-8/fable-5 (claude + anthropic ids)
Pre-4.7 models (Opus 4.6/4.5, Sonnet, Haiku) keep manual budgets and sampling params.
TDD: +27 cases (adaptive-thinking-normalize, sampling-params, translator). typecheck/lint/file-size green.
* docs(changelog): note Claude Opus 4.7+ adaptive-thinking + fixed-sampling fixes (#4230)
Integrated into release/v3.8.29 (round 7). Media serviceKinds derived from backend registries (single source of truth) unioned with declared kinds; listing + detail pages use the same resolver (no 'listed but 404'). Surfaces MiniMax (tts/video/music) + ~48 previously-invisible media providers. Validated locally: 6/6 tests + typecheck:core clean.
Integrated into release/v3.8.29 (round 7). TPROXY decrypt 4b/N: local-only route (/api/tools/agent-bridge/tproxy, LOCAL_ONLY per routeGuard) + dedicated trust-store CA installer (execFileWithPassword, no shell). Validated locally: 12/12 tests + typecheck:core clean (test-unit does not run on release PRs).
Singleton lifecycle around the decrypt-capable transparent listener (#4200):
builds the dynamic CA (#4173), gates on native-addon availability, holds the
single running handle, counts interceptions, and exposes start / stop / status
for a local-only route (4b) to drive.
The OS trust-store mechanism is deliberately not baked in: installCa / uninstallCa
are injected by the caller (the route / VPS e2e provides the real installer, which
needs its own trust-store slot so it does not clobber the static MITM cert). The
manager therefore never mutates the trust store itself and stays unit-testable
without root or the native addon.
- startCaptureMode: rejects when unavailable or already running; builds the CA;
wires the injected installCa/uninstallCa through to the decrypt listener.
- stopCaptureMode: stops the handle (close + uninstall CA + revert), idempotent.
- getCaptureStatus: running / available / startedAt / interceptCount / onPort.
Route + real trust-store installer + Traffic Inspector UI tab + VPS
e2e-with-decrypt + addon prebuilds follow (4b+).
* fix(compression): bound mcpAccessibility maxTextChars on the live read path
smartFilterText reserves 300 chars for the truncation tail, so a maxTextChars at/below that
makes headSize <= 0 and the whole tool result is replaced by the notice (total data loss). Two
gaps let a bad value reach the engine: the DB normalizer floored maxTextChars only at > 0, and
the live MCP-server read path (readMcpAccessibilityConfig) did a raw {...DEFAULT, ...parsed}
spread with no bounding at all.
Centralize the floors in a shared clampMcpAccessibilityConfig (engine layer) used by both the DB
normalizer and the server read path; values in (0, 600) fall back to the default. Export the
tail-reserve constant (300) and use it in smartFilterText so the engine and the bounds stay in
sync. The F5.3 headSize >= 0 clamp stays as a second layer.
Defense-in-depth today (the config isn't writable via the settings API yet — making it
configurable is a tracked follow-up in the compression-completeness work); this closes the
data-loss path for when it lands.
Part of the compression "100% functional" program (audit follow-up).
* chore(quality): bump server.ts file-size baseline 1457->1458 (#4206 +1 import)
The RTK R5 grouping strategy (collapse near-equivalent consecutive lines) is read by the
engine (config.enableGrouping / groupingThreshold) but was unreachable in production: the
rtkConfigSchema (.strict()) rejected the two fields on write and normalizeRtkConfig dropped
them on read, so they could never be anything but undefined.
Add enableGrouping (bool) + groupingThreshold (int, 2..100, default 3) to DEFAULT_RTK_CONFIG,
normalizeRtkConfig, and rtkConfigSchema so the feature is actually settable and survives the
DB round-trip. Default stays OFF — no behavior change for existing configs.
Part of the compression "100% functional" program (audit follow-up).
Integrated into release/v3.8.29. Thanks @megamen32! Reconciled on your branch: the branch had drifted behind release and would have reverted the centralized vision detection (#4072), the no-thinking gateway variants (#4145) and #4164's auto/* loop — those are kept, and your genuine improvement is applied on top: each auto/* /v1/models entry now carries advertised context/output limits + capabilities (createBuiltinAutoCombo, with a fallback to the minimal entry on resolve failure). Added a regression test (Rule #18) and bumped the catalog.ts file-size baseline 1440→1463 with justification.
Integrated into release/v3.8.29 — TLS-terminating capture for TPROXY (decrypt 2/N). New src/mitm/tproxy/tlsCapture.ts: TLS-terminates the intercepted client with a per-SNI leaf from the dynamic CA (#4173), feeds plaintext to an internal http.Server, captures to the Traffic Inspector as source 'tproxy' (sanitizeHeaders + maskSecret), and forwards re-encrypted via an injected seam (realForward uses connectMarked for anti-loop, #4169). Secure-by-default: rejectUnauthorized defaults true; errors via sanitizeErrorMessage; bounded timeouts + closeAllConnections. CaptureSource enum + Zod widened with 'tproxy'. Validated: 17/17 unit tests (real local TLS round-trip + secret masking + 502/error path), targeted typecheck of changed files clean, file-size + test-discovery green.
Integrated into release/v3.8.29 — import-only-free-models connection option + free/paid list filters and free-first sort. Thanks @felipesartori! On review we relocated the 3 React-component tests from tests/unit/ to tests/unit/ui/ so a runner actually collects them (check:test-discovery had flagged them as orphans), bumped the file-size baseline for the two connection modals (cohesive toggle, mirroring #3879/#2997), and synced the branch with the current release. 12/12 vitest + 16/16 node tests green.
qwen-web (cookie provider) had no PROVIDER_MODELS_CONFIG entry, so its model-
discovery page returned an empty/stale local catalog — the OAuth fallback at the
top of the route only fires for provider===qwen, so qwen-web fell through to the
no-config branch.
Added a qwen-web entry that fetches the public https://chat.qwen.ai/api/v2/models
endpoint (no auth header configured/sent) and parses the
{ data: { data: [{ id, name, owned_by }] } } shape, with a flatter { data: [] }
fallback.
This is Problem #3 of #3931 (diagnosed by @thezukiru). Problem #1 (validator
bare-token false-positive) shipped earlier in the merged PR #3958; Problem #2
(empty stream from Qwen WAF bot-detection on the streaming endpoint) is a
separate upstream/stealth concern and stays open.
TDD: tests/unit/qwen-web-models-discovery-3931.test.ts mocks the upstream and
asserts source==='api' + the live ids (and the flatter shape), RED 0/2 ->
GREEN 2/2. Rebaselined route.ts 2512->2531.
Co-authored-by: thezukiru <thezukiru@users.noreply.github.com>
OmniRoute ships a zero-setup auto/* catalog (auto/best-coding, auto/pro-
reasoning, …, 16 AUTO_TEMPLATE_VARIANTS) that the dashboard advertises and that
resolve on demand via createBuiltinAutoCombo, but the /v1/models listing only
emitted persisted DB combos + provider models. Clients that build their model
picker from /v1/models (e.g. Hermes Agent) never saw any auto/* option.
The catalog now emits every AUTO_TEMPLATE_VARIANTS id (owned_by: combo) at the
top of the list, deduped against persisted combos so a user combo can't shadow
or double a built-in id.
TDD: tests/unit/models-catalog-auto-combos-4164.test.ts asserts every auto/*
variant is advertised, they sit at the top, and ids are unique (RED 2/3 ->
GREEN 3/3). Aligned the existing 'context_length for individual chat models'
test to exclude owned_by:combo entries (combos/routers resolve dynamically and
have no fixed context_length — they are not individual chat models).
models.dev catalogs Pixtral 12B under the short id `pixtral-12b` (attachment:
true, image modality), while requests use the Mistral API alias
`pixtral-12b-latest`. getSyncedCapabilityForResolved tried only exact / raw /
static-spec-canonical ids, all of which miss the short form, so vision fell
through to the #4071 model-id heuristic and `attachment` stayed null.
Add a last-resort fallback that retries the synced lookup with a trailing
`-latest` stripped, so synced metadata wins for these aliases. Models whose
`-latest` id is stored verbatim (e.g. pixtral-large-latest) keep resolving
directly. TDD: tests/unit/model-capabilities-mistral-vision-sync-4073.test.ts
(RED 2/4 -> GREEN 4/4) seeds a synced fixture and asserts the verdict comes via
the synced path (attachment === true/false), not the heuristic.
The models.dev sync remains manual-only (no scheduled refresh) — documented as a
separate follow-up.
Follow-up to #4054. The Request Logger still froze auto-refresh on some hosts
(reported on 3.8.28 Docker, works on 3.8.24). #4054 made the INITIAL visibility
fail-open, but the pause is event-driven: a host that fires a one-shot
visibilitychange->hidden and then keeps reporting 'hidden' (or recovers without
firing the event again) left visibleRef stuck false, so the interval ticked but
never polled - only the manual Refresh button worked.
The poll tick now also re-checks the LIVE document.visibilityState, and a window
'focus' listener re-arms polling (a focused window is a reliable signal the page
is actively viewed). A genuinely backgrounded browser tab still pauses (reports
'hidden' and never receives focus), preserving the #3109 optimization.
TDD: two new jsdom regression tests (RED on current release, GREEN after);
the existing 'pause on real background' test stays green.
Case-by-case follow-up on the 2026-06-17 refresh deferred items. Each dead tier was
re-verified against its official source on 2026-06-18 before flipping (qwen-web lesson):
- aimlapi -> hasFree:false. docs.aimlapi.com/faq/free-tier states "The Free Tier is
currently paused"; now pay-as-you-go (min $20 top-up).
- gitlawb + gitlawb-gmi -> hasFree:false. Free MiMo revoked 2026-05-24 (GitHub
Gitlawb/openclaude#1345 "OPENGATEWAY REVOKED THE ONLY 1 FREE MODEL"); Nemotron promo
ended 2026-06; Opengateway is now a pay-as-you-go credit gateway. Removed 40 stale
gitlawb-gmi catalog records.
- yi -> hasFree:false. Yi-Light retired; platform.01.ai is pay-as-you-go (Yi-Lightning
paid); open weights are download-only. Removed 1 stale catalog record.
- iflytek / sparkdesk -> kept hasFree:true with a ToS-caution freeNote: Spark Lite is
free but the ToS prohibits programmatic extraction / relay to third parties (iflytek
also requires Chinese real-name auth).
- monsterapi -> freeNote tightened to "one-time signup trial" (recurring plan = 0
credits/mo); the budget catalog already classified it one-time-initial.
The dead providers stay registered (paid use still works); only the free-tier
advertisement is removed. FREE_TIERS.md "Removed" note + budget table reconciled.
Tests: gitlawb-provider guard updated (hasFree now false for both gitlawb variants);
discontinued-providers-2026 extended to cover the 4 new flips + an iflytek ToS-caution
assertion. Re-verified live 2026-06-18 (Hard Rule #18).
Mid-stream continuation for truncated streams (FCC port, Task 4.4). Opt-in (STREAM_RECOVERY_MIDSTREAM_ENABLED, default OFF); plain-text OpenAI-compat only, nunca com tool-call; OFF byte-idêntico ao #4131. Integrado em release/v3.8.29.
No-thinking gateway model IDs (FCC port, Fase 8.1): synthetic claude-3-omniroute-no-thinking/<provider>/<model> catalog id that resolves to the real model with reasoning suppressed. Integrado em release/v3.8.29.
Owner found the 46px graph-paper cells a touch large for the dashboard
layout; tightening --grid-size to 32px (a ~30% reduction). Mirrors the
matching change on the marketing site so the two stay identical.
Guard test + design.md updated. Build verified: --grid-size compiles to 32px.
Follow-ups from the 2026-06-17 free-tier refresh (#4089) that did NOT land in the
original merge — orphan commit ccc13c27c was pushed after the PR had already merged,
so these three follow-ups never shipped. Re-applied onto release/v3.8.29.
- providers.ts: hasFree flipped to false for confirmed-dead free tiers
(phind x2, kluster, glhf, chutes) so onboarding stops advertising a free tier
that no longer exists. Deliberately NOT flipped: gitlawb (dedicated test guards
hasFree:true), aimlapi/theoldllm (medium-confidence research) — flipping risks a
qwen-web-style false removal.
- New test tests/unit/discontinued-providers-2026.test.ts guards both the flips and
the intentional keeps.
- Headline rounded ~1.5B -> ~1.6B in README (hero/badge/intro/bullet), FREE_TIERS
honest-headline sentence, and FREE-TIERS-GUIDE total. The TL;DR table + live card +
SVG keep the precise computed ~1.54B.
- FREE_TIERS per-provider table regenerated from the per-model catalog (pool-deduped,
2026-06-17): the old inflated rate-limit rows (sparkdesk 2.59B / tencent 2.07B /
siliconflow 1.73B) are gone — uncapped providers now show 'uncapped*'.
Grid wallpaper opacity tuned up so it's actually visible on the dense
dashboard: light 0.045->0.07, dark 0.035->0.06 (the site's 0.045/0.035
read as invisible behind the cards/chrome that cover most of the viewport).
Focus-ring reconcile (design.md C6): the form controls (Input, Select,
Textarea, Toggle, Checkbox) now focus on the accent (violet) ring to match
the global --focus-ring, instead of the red ring-primary that collided with
the red error state. Error rings stay red.
Guard test updated (grid opacity + new C6 assertion). Build verified:
--grid-line compiles to #00000012 / #ffffff0f and focus:ring-accent/30 +
focus:border-accent/50 utilities are generated.
Unified visual identity (design phases 1-4): grid, primitives, tables, form controls. Integrado em release/v3.8.29: clsx + tailwind-merge adicionados à dependency-allowlist (já declarados no package.json, consumidos por src/shared/utils/cn.ts) para o check:deps anti-slopsquatting passar.
Free DuckDuckGo web search as last-resort provider (FCC port, Fase 6). Integrado em release/v3.8.29: conflito de file-size-baseline.json resolvido (união dos rebaselines duckduckgo_free + stream_recovery) e teste de contagem de providers atualizado 12->13.