mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
feat/plugin-browser-pool
33 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3238df3204 |
perf: lazy provider init, P2C quota cache, structuredClone elimination, getSettings→getCachedSettings (batch 2) (#7893)
* perf: startup parallelization, stream TextEncoder lift, auth middleware bottlenecks
Startup (~100-300ms faster cold start):
- Parallelize 4 early imports via Promise.all() in registerNodejs()
- Parallelize 10 independent background services via Promise.allSettled()
- Each service has independent try/catch — no failure domino effect
Streaming pipeline (8 fewer TextEncoder GC allocations per SSE event):
- Lift new TextEncoder() from per-chunk inside buildClaudeStreamingResponse
to function scope alongside existing decoder singleton
Auth middleware bottlenecks (from PerfBottleneckAnalysis):
- Backoff decay loop: replace updateProviderConnection (full CRUD:
SELECT+encrypt+cache-invalidate+backup) with resetConnectionBackoff
(targeted UPDATE of backoff/error columns only)
- Dual .filter() for quota: replace two passes calling
isQuotaExhaustedForRequest per connection with a single for loop
partitioning into withQuota/exhaustedQuota
- Debug-log filter recomputation: capture connectionFilterStatus Map
during the filter pass; debug loop reads 6 string comparisons instead
of 6 function calls per connection
Supporting:
- Add resetConnectionBackoff to src/lib/db/providers.ts (patterned after
clearConnectionErrorIfUnchanged, no CAS check)
- Re-export resetConnectionBackoff from src/lib/localDb.ts
- Update integration-wiring.test.ts regex for parallelized dynamic import
* perf: P2C quota cache, lazy provider init, structuredClone elimination, getSettings→getCachedSettings
- **auth.ts: P2C quota re-evaluation cache** — quotaResults Map threaded
through selectPoolSubset → compareP2CConnections → getP2CConnectionScore.
Populated during filter + partition passes, eliminating redundant
evaluateQuotaLimitPolicy / isQuotaExhaustedForRequest calls when the
P2C comparator re-evaluates previously-scored connections.
- **constants.ts: lazy PROVIDERS via Proxy** — replaces eager
generateLegacyProviders() + loadProviderCredentials() at module load
with Proxy delegating to deferred init on first property access.
- **providerModels.ts: lazy PROVIDER_MODELS + PROVIDER_ID_TO_ALIAS** —
same Proxy pattern for both exports; generateModels()/generateAliasMap()
deferred until first read.
- **stream.ts: structuredClone → minimal object spread** — replaces
O(n) deep clone of SSE response chunks with targeted reconstruction
of only mutated fields (usage, delta.content, finish_reason).
- **progressTracker.ts: TextDecoder lift** — module-level decoder
instead of per-chunk new TextDecoder().
- **Route files: getSettings() → getCachedSettings()** — 13 API route
files converted from uncached per-request DB reads to TTL-cached
wrapper (5s default), eliminating redundant queries on every request.
- **settings.ts: re-export getCachedSettings** from readCache for
non-localDb consumers.
- **Remove settingsCache.ts** — dead file, no imports reference it.
TS compile: 0 errors. Auth tests: 225/225 pass. Services: 269/269 pass.
* perf: Phase 1 tangible wins — egressCache eviction, mmap_size PRAGMA, composite indexes, proxyFallback lazy import
- egressCache: lazy TTL eviction on getCachedEgressIp access (bounds memory
leak to distinct proxy URLs, typically <100)
- mmap_size: apply stored PRAGMA from key_value table (256MiB default) after
applyStoredDatabaseOptimizationSettings — setting was stored but never applied
- schemaColumns: add idx_uh_provider_model_timestamp (covers getModelLatencyStats)
and idx_pc_provider_auth_type (covers 6+ provider_connections queries)
- proxyFallback: convert static import to dynamic import() inside error handler
(defers 210ms module load from startup to first proxy-retry scenario)
* perf: add dedup expression index, unref() sweep timers
- Add COALESCE expression index idx_uh_dedup on usage_history
matching the exact dedup query pattern. Eliminates FULL TABLE
SCAN on every saveRequestUsage insert.
- Add composite idx_uh_provider_model_timestamp on usage_history.
- Add composite idx_pc_provider_auth_type on provider_connections.
- Add .unref() to setInterval in batchProcessor.ts (polling loop).
- Add .unref() to setInterval in runtimeHeartbeat.ts (heartbeat).
* perf: bump SQLite cache_size default from 16MB to 64MB
New installs now start with 64MB page cache (was 16MB). Existing
users' stored settings are unchanged. Reduces disk reads for the
typical ~250MB database by keeping ~25% of pages in memory.
Also resolved pre-existing merge conflict in webhooks.ts.
* docs: add Redis production config guide and proxy port clash investigation report
- docs/redis-production-config.md: comprehensive Redis tuning guide
covering client options, server config, Docker settings, scaling,
and monitoring for all three Redis workloads (rate limiting,
auth cache, quota store)
- docs/proxy-port-clash-report.md: investigation confirming proxy
subsystem has no port binding issues; real EADDRINUSE history
traced to process supervisor crash-loop restart race (#4425) and
live-dashboard port clash (#6324), both already fixed
* fix: address PR #7893 review — add Proxy traps, extract migrations to reduce providers.ts size
- Add set trap to PROVIDER_ID_TO_ALIAS Proxy (providerModels.ts)
- Add deleteProperty traps to all three lazy Proxies (PROVIDERS,
PROVIDER_MODELS, PROVIDER_ID_TO_ALIAS)
- Extract autoMigrateLegacyEncryptedConnections and getGheCopilotHosts
from providers.ts (1129→1036 lines, -93) into providers/migrations.ts
- Both functions re-exported via providers.ts for backward compat
File-size ratchet resolved: src/lib/db/providers.ts now 1036 lines.
* fix: resolve merge conflict markers in 3 route/test files
- model-combo-mappings/route.ts: kept upstream version (Zod pagination
via validateBody + isValidationFailure), restored missing return
statement for GET handler
- playground/presets/route.ts: kept stashed version details (satisfies
type-narrowing + inlined Response) — functionally identical
- error-sanitization.test.ts: matches upstream exactly (no diff)
Test verification: same 7 pre-existing failures confirmed on upstream
baseline (
|
||
|
|
6f57c88de1 |
fix(sse): silence noisy proxy-failure log on caller-initiated abort (#7266)
* fix(sse): silence noisy proxy-failure log on caller-initiated abort The pinned-proxy dispatch path in proxyFetch.ts logged every failure — including a plain caller abort or the caller's own AbortSignal timeout firing — as "[ProxyFetch] Proxy request failed ... fail-closed". A client cancelling its own request is not a proxy transport failure and shouldn't be misreported as one in ops logs/alerting; it still propagates to the caller unchanged (fail-closed behavior is untouched). Co-authored-by: TuyulSpam <287281626+TuyulSpam@users.noreply.github.com> Inspired-by: https://github.com/decolua/9router/pull/2589 * chore(changelog): fragment for #7266 --------- Co-authored-by: TuyulSpam <287281626+TuyulSpam@users.noreply.github.com> |
||
|
|
b729a8f273 |
Release v3.8.43 (#5609)
* chore(release): open v3.8.43 development cycle * docs(relay): clarify backend routing contract (#5621) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). * fix(security): avoid rendering error stacks (#5624) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). * fix(chatgpt-web): restore dot-form Pro model ids (#5549) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). * feat(commandCode): add multimodal image support for CC vision models (#5557) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). * fix(providers): validate M365 Copilot web credentials (#5432) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). * fix(sse): bound chat hot-path heap — pressure-aware admission + response cap + clone reductions (#5152) (#5425) Integrated into release/v3.8.43 (drift-shed: cherry-picked the real change onto the release tip; stale-base drift dropped). * fix: model lockout not recording for 429 rate_limit_exceeded from Antigravity ## Problem When Antigravity returns HTTP 429 with `rate_limit_exceeded` error code, the model lockout system never records the failure, so the model is not cooled down despite being rate-limited. ### Root Cause Antigravity's 429 error text is: `"Resource has been exhausted (e.g. check quota)."` The QUOTA_PATTERNS in `classify429.ts` contained overly broad regexes: - `/resource.*exhaust/i` — matches "Resource has been exhausted" - `/check.*quota/i` — matches "check quota" This caused `classifyErrorText()` to return `QUOTA_EXHAUSTED` (wrong), which set `providerExhausted = true` in the combo target exhaustion logic. With `providerExhausted`, the retry path was skipped entirely, and while the "done retrying" path should still record lockout, the misclassification cascaded into incorrect provider-level exhaustion state. Additionally, `targetExhaustion.ts` used the raw error text string instead of the structured error code (`rate_limit_exceeded`) that was already parsed from the response body. ## Fix 1. **classify429.ts** — Removed overly broad `/resource.*exhaust/i` and `/check.*quota/i` from QUOTA_PATTERNS. Antigravity's rate-limit wording is not a true quota exhaustion signal. 2. **targetExhaustion.ts** — Added optional `structuredError` to `ApplyComboTargetExhaustionOptions`. When available, the structured error code (e.g. `rate_limit_exceeded`) takes precedence over raw error text for exhaustion classification. 3. **combo.ts** — Passes `structuredError` to both `applyComboTargetExhaustion` call sites (dispatch path + retry-or-rotate path). ## Effect `structuredError.code = "rate_limit_exceeded"` → classified as rate-limit (not quota) → `providerExhausted = false` → retry proceeds → `recordModelLockoutFailure` called → model enters lockout with proper cooldown (120s base, exponential backoff). ## Tests Added 2 new tests for `structuredError.code` precedence in exhaustion classification. All 28 related tests pass. * fix(checks): normalize route paths on windows (#5613) Integrated into release/v3.8.43. Windows path-normalization fix for the route-guard membership gate + regression test (Rule #18). Co-authored test added by maintainer. * fix: truncate tool list when provider limit exceeds MAX_TOOLS_LIMIT (grok-cli 200) - Add proactive PROVIDER_TOOL_LIMITS map with grok-cli: 200 - Fix regex to capture 'maximum is 200' (not '427 tools provided') - Remove broken truncation gate that skipped limits >= MAX_TOOLS_LIMIT (128) - Add tests for Grok regex, proactive limits, and limits above threshold Refs #5563 * test(chatcore): cover grok-cli tool-list truncation via prepareUpstreamBody (#5563) Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(security): v3.8.15 hardening follow-ups (Seg2/Seg3/Seg4/Bug3) (#5512) Security v3.8.15 hardening follow-ups: Seg2 (CHANGEME boot warn), Seg3 (auth_token cookie maxAge 30d), Seg4 (VS Code path-token once-per-process warning), Bug3 (real global install path resolution), Bug1 (segment-match node_modules in auto-update detection). All 5 carry TDD regression guards. * Fix HuggingChat web session routing (#5592) (#5592) Integrated into release/v3.8.43. HuggingChat web session-routing fix (root parent-message fetch + cookie propagation + encrypted-credential guard) + 24-model catalog refresh. Maintainer adjustments (co-authored): reverted the freeModelCatalog.data.ts whole-file reformat down to the surgical 24-record huggingchat change (preserving the auto-generated compact format), and added a 502 regression test for the null parent-message-id path (Rule #18). * fix: preserve system role for GLM 5.1/5.2 (#5610) (#5663) * fix: restore Codex Responses WS TLS profile + apply proxy (#5591, #5611) (#5668) * fix: allow saving providers without a live validator (#5565, #5567) (#5669) * fix: static model catalog for jules/linkup/ollama/searchapi search providers (#5569, #5571, #5573, #5575) (#5672) * fix: live AI/ML API catalog + deprecate dead CablyAI (#5570, #5568) (#5673) * fix: correct 404 provider setup links for ollama/searchapi/you.com (#5572, #5574, #5576) (#5674) * fix: page call_logs cleanup queries to avoid startup OOM on large DBs (#5618) (#5675) * fix: use PowerShell Expand-Archive on Windows for embedded-service install (#5590) (#5678) * fix: treat array content blocks as valid output in detectMalformedNonStream (#5559) (#5680) * fix: render memory engine status detail strings in English (#5596) (#5685) * fix: free proxy pool silent sync failure — iplocate txt + per-source isolation + surface errors (#5595) (#5686) * chore(quality): close QG v2 tail — drop orphan semcheck.yaml + Fase 9 maturity re-eval (#5681) - Remove semcheck.yaml: orphan config (zero workflow/script wiring) with stale rule counts; deterministic doc-accuracy coverage already exists (check:fabricated-docs --strict + docs-counts-sync + docs-symbols). Drop the REPOSITORY_MAP row referencing it. - Add docs/ops/MATURITY_REEVAL.md (Fase 9): re-measures maturity post-Ondas 0-3. The two biggest structural weaknesses from QUALITY_GATE_PLAYBOOK (2026-06-16) are now closed: fast-gates hole (quality.yml runs typecheck:core + impacted TIA unit tests + vitest + shards) and mutation-score-as-ratchet (check-mutation-ratchet.mjs + seeded baseline + nightly blocking job). Residual gap is owner/infra-gated (branch-protection main, SLSA L3, CodeQL advanced). - Record agent-lsp as deferred/opt-in (doc-only scaffold, no wiring). * fix(ci): stabilize nightly-mutation — guard tap.testFiles drift + anti-flake eps (#5682) Root cause (NOT a timeout): the nightly-mutation run fails on cold-cache nights because the blocking mutation-ratchet job measures modules below baseline, while warm-cache nights pass — the verdict tracked GitHub Actions cache state, not code quality. Proven via a local Stryker probe on headers.ts: covering unit tests (no-memory-header, strip-reasoning) had drifted OUT of stryker.conf.json tap.testFiles, so their mutants went covered-but-unkilled = Survived on a cold full run (COVERED score 61.73 vs 94.29 baseline); adding them restores the kills. - Add scripts/check/check-mutation-test-coverage.mjs: guards that every UNIT test importing a Stryker-mutated module is listed in tap.testFiles. Advisory by default, --strict in CI (wired in quality.yml fast-gates). Prevents recurrence. - Add the 38 drifted covering unit tests to stryker.conf.json tap.testFiles (138 -> 176). Monotonically safe: more covering tests only raise/hold the score. - Add MUTATION_RATCHET_EPS (1.0pt) anti-flake tolerance to check-mutation-ratchet so sub-point tap-runner jitter no longer false-fails the gate. Lowers no baseline. - Tests: check-mutation-test-coverage (3) + eps cases in check-mutation-ratchet. Residual: a clean post-merge nightly confirms scores return to/above baseline; any marginal residual gets a baseline re-seed (operator). * refactor(dashboard): split sidebarVisibility god-file into types + sections leaves (#5683) Behavior-preserving decomposition: src/shared/constants/sidebarVisibility.ts 1197 -> 291 LOC by extracting two leaves under sidebarVisibility/: - types.ts (160): HIDEABLE_SIDEBAR_ITEM_IDS + all sidebar types (self-contained). - sections.ts (762): section building-block consts + SIDEBAR_SECTIONS (imports types only — cycle-safe). COMPRESSION_CONTEXT_GROUP + SIDEBAR_SECTIONS stay exported; host re-exports both + 'export *' of types, so every consumer import path is unchanged. Byte-identical data verified via JSON.stringify of HIDEABLE_SIDEBAR_ITEM_IDS / SIDEBAR_ICON_ACCENTS / COMPRESSION_CONTEXT_GROUP / SIDEBAR_SECTIONS / SIDEBAR_PRESETS + getSectionItems output (identical before/after). typecheck:core, check:cycles (no cycles), check:file-size (3 files <800), and the 3 sidebar suites (20/20) pass. No logic changed. Note: file-size frozen baseline for sidebarVisibility.ts (1198) can ratchet to 291 to lock the shrink (left for the release ratchet / operator). * fix: surface fusion-specific config on the Global Routing tab (#5598) (#5688) * fix(executor): route OpenAI-compatible MCP Responses requests to /responses (#5483) Closes #5483. OpenAI-compatible providers receiving a Responses-shaped request carrying MCP / tool_search tools now route to the upstream /responses endpoint instead of downgrading to /chat/completions, preserving Codex deferred tool discovery. Detection helpers extracted to open-sse/executors/forceResponsesUpstream.ts. Thanks to @KooshaPari. * fix(ci): make release-green pre-flight gates visible + bounded so unit reds are not missed (#5644) Integrated into release/v3.8.43. * fix(body-size): raise LLM API payload limit for responses routes (#5652) Integrated into release/v3.8.43. Thanks @JxnLexn! * fix(test): use lightweight health probe for batch e2e (#5651) Integrated into release/v3.8.43. Thanks @KooshaPari! * feat(compression): T05/C5 — preserveSystemPrompt mode enum + legacy back-compat (#5653) Integrated into release/v3.8.43. Includes the legacy-boolean back-compat derivation so existing preserveSystemPrompt=false installs keep whenNoCache behavior. * routing: optimize latency strategy with perf metrics (#5629) Integrated into release/v3.8.43. Thanks @KooshaPari! * feat(db): models/5004 — self-correcting model context-window overrides (#5667) Integrated into release/v3.8.43. * feat(providers): complete SenseNova free Token Plan — chat + Text-to-Image (port from 9router#2233) (#5679) Integrated into release/v3.8.43. * feat(api): routing/4985 — configurable response-body validation + failover (#5684) Integrated into release/v3.8.43. * fix(chatcore): default Claude tool type to "custom" when missing (#5662) Integrated into release/v3.8.43. Port from 9router#2196. Co-authored-by: warelik <warelik@users.noreply.github.com> * fix(translator): merge consecutive same-role contents for Gemini (port from 9router#2191) (#5661) Integrated into release/v3.8.43. Port from 9router#2191. * chore(bun): add locked bun runtime dependency (#5615) Integrated into release/v3.8.43. Bun 1.3.10 pinned via npm lockfile (adopt-partial decision). Thanks @KooshaPari! * chore(bun): run validated ts scripts with bun (#5612) Integrated into release/v3.8.43. Thanks @KooshaPari! * chore(bun): run CI script checks with bun (#5617) Integrated into release/v3.8.43. Validated bun==node output for all 3 gates (provider-consistency, compression-budget, known-symbols). Thanks @KooshaPari! * fix(build): make pack validator bun safe (#5643) Integrated into release/v3.8.43. Forward-compat guard; node/npm path unchanged. Thanks @KooshaPari! * docs: document Bun as the allow-listed build/dev script runner (Node stays the published runtime) (#5703) Integrated into release/v3.8.43. * feat(analytics): show $0 cost for flat-rate subscription/cookie providers (#5552) (#5704) * refactor(api): extract unified-catalog helpers into cohesive leaf modules (#5699) BLOCO E2 of the god-files campaign. The module-level pure/standalone helpers in src/app/api/v1/models/catalog.ts (1611 LOC) were lifted out verbatim into five cohesive leaf modules so the catalog host shrinks toward the 800-LOC file-size cap without any behavior change (host now 1345 LOC; the heavy getUnifiedModelsResponse orchestrator is untouched — its in-function closures stay put): - catalogHelpers.ts — pure numeric/array/shape helpers + shared catalog types - catalogOpenrouter.ts — OpenRouter id/modality/free-model/display-name helpers - catalogVision.ts — vision-capability field derivation (+ isVisionModelId re-export) - catalogProviderMaps.ts — alias<->providerId resolution maps (buildAliasMaps) - catalogRequest.ts — /v1/models API-key auth gating + Codex CLI client detection The host re-exports getCustomVisionCapabilityFields and isVisionModelId so the public API consumed by other tests (llm-selector-custom-vision-models, vision-detection- consistency) is unchanged; all 9 catalog/vision suites stay green. Adds tests/unit/catalog-helpers-extraction.test.ts: characterization tests for every extracted helper + a guard asserting the host preserves its public exports. Validated: typecheck:core, 50 catalog characterization tests, 12 new leaf tests, integration-wiring, check:cycles, check:file-size (no new violations), ESLint, Prettier. * feat(mcp): T07 — expose RTK learn/discover as MCP tools (#5691) Adds two read-only MCP tools wrapping the existing RTK discovery primitives: omniroute_rtk_discover (discoverRepeatedNoise/suggestFilter over recently captured raw tool output → candidate noise patterns + suggested filter) and omniroute_rtk_learn (listRtkCommandSamples + commandToId). Scope read:compression, MCP audit-logged, no new engine logic. Regression guard: tests/unit/compression/rtk-mcp-tools.test.ts. gaps v3.8.42 — T07. * feat(compression): T05/C3 — opt-in LLM-tier compression engine (#5702) Adds an opt-in, default-off LLM-tier compression engine ('llm') that condenses non-system message prose via a pluggable chat-completion backend, mirroring the llmlingua contract. Safe by construction: no-op default backend (pass-through out of the box), not in the default stacked pipeline, enabled defaults false, fenced code blocks + system messages never sent to the model, fail-open everywhere, minTokens floor. Real production backend is a VPS-validated follow-up (Hard Rule #18). Regression guard: tests/unit/compression/llm-compressor-engine.test.ts (8). gaps v3.8.42 — T05/C3. * refactor(db): extract compat/aliases/mitm helpers from db/models.ts into leaf modules (#5705) BLOCO E3 of the god-files campaign. db/models.ts (1250 LOC) mixed six concerns; the three cleanly-separable ones plus the shared key_value helpers were lifted out verbatim into a new src/lib/db/models/ subdirectory, leaving the tightly-coupled custom/synced/ flags trio in the host (host now 936 LOC). The host re-exports every moved public symbol so the module's public API (consumed by ~29 test files + localDb) is unchanged. - models/shared.ts — asRecord / toNonEmptyString / getKeyValue + JsonRecord (19 LOC) - models/compat.ts — model-compat overrides + sanitizeUpstreamHeadersMap (249 LOC) - models/aliases.ts — model-alias CRUD + cascade delete (61 LOC) - models/mitmAlias.ts — MITM alias get/set (32 LOC) The custom/synced/flags trio stays in the host because it is genuinely coupled (flags->getCustomModelRow, flags->readCompatList, custom->removeModelCompatOverride, synced->getModelIsDeleted, setModelIsHidden->updateCustomModel) — splitting it cleanly is a follow-up. Dependency DAG is acyclic (verified by check:cycles). Adds tests/unit/db-models-split.test.ts: characterization of the pure extracted helpers + a guard asserting the host preserves its full public export surface. Validated: typecheck:core, check:cycles (no cycles), 77 existing db/models consumer tests (db-models-crud/extended/aliases-cascade + 7 more) green, 7 new tests, ESLint, Prettier, check:file-size (host 936 < frozen 1259; no new violations). * refactor(db): extract pricing/lkgp/cache-metrics from db/settings.ts into leaf modules (#5709) BLOCO E3 of the god-files campaign. db/settings.ts (1154 LOC) mixed five concerns; the three cleanly-separable ones plus the shared toRecord/JsonRecord helper were lifted out verbatim into a new src/lib/db/settings/ subdirectory, leaving the Settings-core + Proxy config concerns in the host (host now 646 LOC). The host re-exports every moved public symbol so the module's public API (consumed by ~93 test files + localDb) is unchanged. - settings/shared.ts — toRecord + JsonRecord (9 LOC) - settings/pricing.ts — pricing layers/sources/per-model + update/reset (254 LOC) - settings/lkgp.ts — Last-Known-Good-Provider get/set/clear (49 LOC) - settings/cacheMetrics.ts — cache metrics + trend (235 LOC) Settings-core + the Proxy-config concern stay in the host: proxy is the most tangled (245-line resolveProxyForConnection, resolution cache, imports from ./proxies) and getSettings is the most central function — leaving them is the correct coupled-core stop. Pricing/LKGP/Cache have NO dependency on Settings/Proxy helpers (verified); the dependency DAG is acyclic (check:cycles). Adds tests/unit/db-settings-split.test.ts: characterization of the shared toRecord helper + a guard asserting the host preserves its full public export surface. Validated: typecheck:core, check:cycles (no cycles), 149 existing+new db/settings consumer tests green (db-settings-crud/extended, 8 pricing suites, cache-metrics, 2 proxy-resolution suites + 29 new), ESLint, Prettier, check:file-size (host 646 < frozen 1155). * fix(translator): re-apply lost defensive hardening for Gemini merge + Claude tool defaults (#5706) Re-applies two dropped gemini-code-assist hardening fixes (defaultClaudeToolType non-object passthrough; mergeConsecutiveSameRoleContents shallow-copy) with regression tests. Follow-up to #5661/#5662. Integrated into release/v3.8.43. * feat(codex): generate fallback profiles for compatible models (#5701) setup-codex now generates Codex profiles for compatible text models from the live /v1/models catalog when the model id doesn't match a hand-tuned pattern, skipping media/embedding models. Integrated into release/v3.8.43. * docs(changelog): credit @Chewji9875 for #5563 + #5579 Add CHANGELOG credit bullets for grok-cli tool-limit (#5563) and Antigravity 429 lockout (#5579). Documentation-only. * test(dashboard): repoint sidebar quota-share placement scan to sections.ts (#5711) The D1 god-file split (#5683) moved the nav-item id definitions out of src/shared/constants/sidebarVisibility.ts into the extracted leaf src/shared/constants/sidebarVisibility/sections.ts. This source-scan test still read the old monolith path, so it found 0 occurrences of id: "costs-quota-share" and failed (base-red on release/v3.8.43). Repoint SIDEBAR_PATH to sections.ts where the ids now live. All four placement assertions (quota-share after quota, same array, far from costs-budget, exactly one occurrence) hold against the new source. * refactor(db): extract columns/nodes/rate-limit leaves from db/providers.ts (#5714) db/providers.ts was a 1106-line god-file mixing four concerns. Extract the three acyclic, cohesive slices into sibling leaf modules under src/lib/db/providers/, leaving the tightly-coupled connection-CRUD core in the host: - providers/columns.ts (116) 10 pure column-normalizer helpers (DB-free) - providers/nodes.ts (163) 6 provider-node CRUD functions - providers/rateLimit.ts (177) 6 rate-limit/quota runtime helpers + formatResetCountdown Host providers.ts: 1106 -> 719 lines. The connection-CRUD core does not call any node or rate-limit function (verified), so the host re-exports the 12 moved public symbols via `export { ... } from './providers/<leaf>'` — the module's public API stays IDENTICAL (23 symbols). Bodies moved verbatim (byte-identical); the only edit to a moved line is the added `export` on the 10 previously-private normalizers. Behavior-preserving: 122 existing provider/quota/rate-limit consumer tests stay green; new tests/unit/db-providers-split.test.ts guards the re-export barrel + characterizes the pure column helpers (38 assertions). Refs #3501 (god-file structural shrink). * refactor(db): extract types + pure mappers from db/proxies.ts (#5717) db/proxies.ts was a 1059-line god-file. Extract the two acyclic, DB-free slices into sibling leaf modules under src/lib/db/proxies/, leaving the tightly-coupled CRUD + assignment + resolution core in the host: - proxies/types.ts (65) 10 proxy type/interface declarations - proxies/mappers.ts (180) pure row mappers / scope normalizers / payload coercers (toRecord, mapProxyRow, mapAssignmentRow, isRelayProxyType, extractRelayAuth, toRegistryProxyResolution, normalizeScope, normalizeAssignmentScopeId, toLegacyProxyLevel, coerceProxyPayload, redactProxySecrets) Host proxies.ts: 1059 -> 847 lines. The resolution functions call createProxy/assignProxyToScope, so the CRUD+resolution core CANNOT be extracted without an import cycle and stays in the host. The host re-exports the 2 moved public functions (extractRelayAuth, redactProxySecrets) via `export { ... } from './proxies/mappers'` — the public API stays IDENTICAL (20 functions; no types were ever publicly exported). Bodies moved verbatim; the only host edits are the new leaf imports, the re-export, dropping the now unused `import { decrypt }`, and two prettier line-wrap reflows of retained ternary/union lines (token-identical). Behavior-preserving: 69 existing proxy/registry/relay/family consumer tests stay green; new tests/unit/db-proxies-split.test.ts guards the re-export barrel + characterizes the pure mappers (35 assertions). Refs #3501. * refactor(db): extract static migration data tables from migrationRunner.ts (#5721) migrationRunner.ts (1124 lines, frozen-baselined) is the startup migration orchestrator. As a conservative, zero-behaviour-risk first slice, extract the six static migration-compatibility DATA tables (verbatim) into a pure-data leaf, leaving the entire orchestrator + all SQL-running helpers in the host: - migrationRunner/constants.ts (118) RENAMED_MIGRATION_COMPATIBILITY, LEGACY_VERSION_SLOT_MIGRATIONS, SUPERSEDED_DUPLICATE_MIGRATIONS, PHYSICAL_SCHEMA_SENTINELS, INITIAL_SCHEMA_SENTINELS, OPTIONAL_FTS5_MIGRATION_VERSIONS Host migrationRunner.ts: 1124 -> 1023. The runtime fts5SupportCache (a WeakMap, mutable state) stays in the host. No public API change (these consts were module-internal). Data moved byte-identical (sed-extracted, verbatim verified); the only host edits are the leaf import + one prettier collapse of a pre-existing 2-line union type annotation to 1 line (token-identical, typecheck-confirmed). Characterize-first (operator-chosen): the existing db-migration-runner.test.ts (26 tests) + no-migration-collisions/weak-rng-fixes/check-db-rules (11) prove the reconciliation/dedup/already-applied BEHAVIOUR is unchanged; the new tests/unit/db-migrationrunner-constants-split.test.ts (7 tests) PINS THE DATA (counts + shape + spot-checks of every table) so a dropped/transposed row is caught immediately. Refs #3501. * refactor(db): extract pure SQL-source builders from usageAnalytics.ts (#5722) usageAnalytics.ts (924 lines, frozen-baselined) mixes two pure SQL-source builders with ~20 getXxxRows() query functions. Extract the contiguous, DB-free builder block verbatim into a leaf, leaving every query function in the host: - usageAnalytics/sources.ts (208) AnalyticsParams, BuildUnifiedSourceOptions, UnifiedSourceResult + buildUnifiedSource + buildPresetUnifiedSource (pure string builders; no DB, no imports) Host usageAnalytics.ts: 924 -> 723. The query functions do not call the builders (callers build the unified source then pass the string in), so the host re-exports the 5 moved public symbols (2 fns + 3 types) and imports AnalyticsParams as a type for its query signatures — the public API stays IDENTICAL (39 symbols). Builder bodies moved byte-identical; the two orphaned section-header banners that described the moved block were removed with it; the retained query-function suffix is byte-identical to the original. Behavior-preserving: 37 existing analytics consumer tests stay green (usage-analytics 12, usage-endpoint-dimension 3, db-usage-analytics-3500 22); new tests/unit/db-usageanalytics-split.test.ts (25 assertions) characterizes buildUnifiedSource's needsAggregated branching (raw-only vs raw+daily_usage_summary) + guards the 39-symbol re-export barrel. Refs #3501. * docs(readme): refresh metrics, list 17 strategies, add Quota-Share + real provider logos - Unify provider count to 236; MCP tools 87->94; cloud agents 3->4 (+Cursor); compression 9->10 engines (+relevance) - Tests -> 21,000+ across 2,586 files; footer -> v3.8.43 - Raise lower bounds to real values: 90+ free, 80+ commands, 24+ CLIs - Language flag grid 33->43 (15/14/14, all locales) - List all 17 routing strategies; new Quota-Share section before Resilience - Real provider logos (lobe-icons + local agentrouter) in providers grid and Free Forever - Top Contributors: refreshed stats + add herjarsa; 280+ title; half-size avatars; contrib.rocks 100->200 - Acknowledgments: refreshed star counts; fix headroom repo rename * docs(readme): update provider counts and add new badges * feat(memory): T10/TV6 — opt-in typed memory decay (#5723) Opt-in typed memory decay so the conversational memory store self-prunes stale episodic noise. access_count + last_accessed_at telemetry (migration 111) is always-on/non-destructive; the sweep is opt-in (MEMORY_TYPED_DECAY_ENABLED, default false). Only episodic decays by default (30d); factual/procedural/semantic immune; access_count>=3 earns immunity; deletions reuse deleteMemory (SQLite+vec+Qdrant in sync), fail-open. Regression guard: tests/unit/memory/typed-decay.test.ts (15). gaps v3.8.42 — T10/TV6. * feat(dashboard): T06/T03 — drag-reorder compression pipeline editor + studio e2e (#5727) T06: named-combos editor gains a @dnd-kit/sortable drag-to-reorder stacked pipeline backed by a pure model (compressionPipelineModel.ts: add/remove/move/update, engine->intensity invariant, never-empty). CompressionPipelineEditor.tsx replaces the inline fixed list in CompressionCombosPageClient; order persists via the existing combos endpoint (no API change). T03: adds tests/e2e/compression-studio.spec.ts (Tela A render + Play/Compare tab switch), the dedicated compression-studio e2e combo-live-studio.spec.ts did not cover. TDD: compression-pipeline-model.test.ts (11) + compression-pipeline-editor.test.tsx (4). gaps v3.8.42 — T06 + T03. * fix(thinking): wire Thinking-Budget boot hydration into live instrumentation path (#5312) (#5729) hydrateThinkingBudgetConfig was only called from the unused src/server-init.ts, which never runs in production, so the dashboard Thinking-Budget mode silently reverted to passthrough on every restart. Wire it into the real boot path (src/instrumentation-node.ts), next to the Global System Prompt restore. Surfaced by live Anthropic-OAuth validation on the VPS (fix A of #5312 was non-functional even though its direct unit test passed). New guard tests/unit/thinking-budget-boot-wiring-5312.test.ts asserts the production boot module calls the hydration, closing the test gap that let this ship. * refactor(usage): extract pure formatting helpers from callLogs.ts (#5725) callLogs.ts (996 lines, frozen-baselined) mixes pure log-formatting / sanitization helpers with DB CRUD, disk-artifact, and rotation logic. Extract the ten pure, DB-free helpers verbatim into a leaf, leaving all stateful code in the host: - callLogs/format.ts (129) asRecord, toNumber, toStringOrNull, truncateText, parseInlineError, normalizeDetailState, sanitizeErrorForLog, toStoredErrorSummary, protectPipelinePayloads, buildRequestSummary Host callLogs.ts: 996 -> 885. The stateful generateLogId (mutates logIdCounter) stays in the host. These helpers were all module-internal, so the public API is unchanged (10 exported functions). Bodies moved byte-identical; the host's now unused 'sanitizePII' import (only referenced inside the moved bodies) moved to the leaf; prettier wrapped buildRequestSummary's signature across lines once the 'export' prefix pushed it past 100 cols (token-identical). Behavior-preserving: 46 existing call-log consumer tests stay green (call-log-cap 14, pagination 4, file-rotation 5, log-retention 5, startup 1, oom 2, trim-sql 2, db-settings-maintenance 13); new tests/unit/calllogs-format-split.test.ts (26 assertions) characterizes the pure helpers + guards the 10-function public API. Refs #3501. * refactor(usage): extract pure stat/coercer helpers from usageHistory.ts (#5728) usageHistory.ts (987 lines, frozen-baselined) mixes pure DB-free helpers with an in-memory pending-request state machine and DB CRUD. Extract the contiguous pure block verbatim into a leaf, leaving all stateful code in the host: - usageHistory/helpers.ts (85) asRecord, toStringOrNull, normalizeServiceTier, toNumber, percentile, stdDev, truncatePendingPreview (+ its MAX_PREVIEW_* bounds, co-located) Host usageHistory.ts: 987 -> 916. The pending-request state machine (module Maps + track/update/finalize/sweep) and DB CRUD stay in the host. These helpers were all module-internal, so the public API is unchanged (21 direct exports + the pre-existing getCompletedDetails re-export = 22). Bodies moved byte-identical (leaf 0 non-verbatim lines); the host's local 'type JsonRecord' moved with the bodies that used it (host no longer references it — typecheck-confirmed). Behavior-preserving: 38 existing usage-history consumer tests stay green (usage-history-db 5, api-key-usage-limits 6, log-retention 5, usage-endpoint-dimension 3, provider-request-failure-pipeline 6, database-settings-maintenance 13); new tests/unit/usagehistory-helpers-split.test.ts (30 assertions) pins the percentile/stdDev formulas + normalizeServiceTier + guards the public API. Refs #3501. * refactor(usage): extract pure quota-normalize helpers from providerLimits.ts (#5730) providerLimits.ts (954 lines, frozen-baselined) is the heavily DB/network-coupled provider quota sync module. Extract a small, fully SELF-CONTAINED leaf of pure quota-key/quota-value normalization helpers (+ the isRecord type guard they share), leaving all sync/DB/network code in the host: - providerLimits/quotaNormalize.ts (72) isRecord, isUsageQuotaKeyAllowed, normalizeUsageQuotaKey, normalizeUsageQuotasForProvider, sanitizeUsageQuotasForProvider Host providerLimits.ts: 954 -> 890. The leaf imports only the external antigravity/agy model-alias helpers the moved bodies reference (moved from the host's import block) — it does NOT import the host, so check:cycles stays clean (no cycle). isRecord (used ~9x in the host) is co-extracted and imported back. These five were all module-internal, so the public API is unchanged (13 exported functions). Bodies moved byte-identical. Behavior-preserving: 18 existing provider-limits consumer tests stay green (sanitize-scope 3, db-provider-limits 3, proxy-fail-closed 3, rotating-expired-guard 7, codex-quota-sync 2); new tests/unit/providerlimits-quotanormalize-split.test.ts (19 assertions) pins isRecord + isUsageQuotaKeyAllowed + guards the 13-function public API. Refs #3501. * refactor(memory): extract pure scoring/conversion helpers from retrieval.ts (#5733) retrieval.ts (1192 lines — ABOVE its 1171 frozen baseline) is the memory retrieval engine (DB + vector + rerank network). Extract the pure, DB-free scoring/conversion helpers (+ the MemoryRow row shape they share) verbatim into a self-contained leaf, leaving all DB/vector/network code in the host: - retrieval/scoring.ts (104) interface MemoryRow + estimateTokens, parseMetadata, rowToMemory, getRelevanceScore Host retrieval.ts: 1192 -> 1072 — back UNDER the 1171 frozen baseline (the split also repairs the pre-existing file-size drift). The leaf imports only ../types, never the host, so check:cycles stays clean (no cycle). MemoryRow moved to the leaf and imported back as a type by the host's DB row functions. The public estimateTokens is re-exported from the leaf; the host also imports it for its internal token-budget loops. The other three helpers were module-internal, so the public API is unchanged (7 exports). Bodies moved byte-identical. Behavior-preserving: 38 existing memory-retrieval consumer tests stay green (rerank 5, hybrid 6, semantic 6, engine-status 9, stats-api 12); new tests/unit/retrieval-scoring-split.test.ts (11 assertions) pins estimateTokens (ceil(len/4)) + parseMetadata + rowToMemory mapping + getRelevanceScore (+20 phrase / +3 token) and guards the public API. Refs #3501. * refactor(sse): extract reasoning-tag detection/extraction from responseSanitizer.ts (#5734) responseSanitizer.ts (1133 lines, frozen-baselined) mixes reasoning-tag detection/extraction with response/usage/streaming sanitization. Extract the cohesive, ZERO-IMPORT reasoning block verbatim into a self-contained leaf: - responseSanitizer/reasoning.ts (143) the reasoning regex consts + collapseExcessiveNewlines, cleanReasoningFragment, splitClosingOnlyReasoningPrefix, movePrefixBeforeContentTagToThinking, extractThinkingFromContent, normalizeReasoningRouteId, isAntigravityReasoningRoute, isTextualReasoningTagNativeRoute, shouldParseTextualReasoningTags Host responseSanitizer.ts: 1133 -> 1003. The block's helpers only call each other, so the leaf has ZERO imports — it cannot import the host (check:cycles clean). The host imports back collapseExcessiveNewlines (6 call sites) + extractThinkingFromContent, and re-exports the two public symbols (extractThinkingFromContent, shouldParseTextualReasoningTags) — the public API stays IDENTICAL (7 exports). Bodies moved byte-identical; two long declarations (REASONING_TAG_FRAGMENT_REGEX, movePrefixBeforeContentTagToThinking signature) were line-wrapped by prettier once the 'export' prefix pushed them past 100 cols (token-identical). Behavior-preserving: 47 existing consumer tests stay green (response-sanitizer 36, strip-reasoning-header 8, textual-toolcall-false-positive 3); new tests/unit/responsesanitizer-reasoning-split.test.ts (11 assertions) characterizes extractThinkingFromContent + shouldParseTextualReasoningTags and guards the public API. Refs #3501. * refactor(sse): extract rate-limit header parsing from rateLimitManager.ts (#5736) rateLimitManager.ts (1034 lines, frozen-baselined) is the stateful rate-limiter (Bottleneck limiters, watchdog timers, learned-limits Map). Extract the pure, ZERO-IMPORT header-parsing block verbatim into a self-contained leaf, leaving all stateful machinery in the host: - rateLimitManager/headers.ts (94) STANDARD_HEADERS, ANTHROPIC_HEADERS, parseResetTime, toPlainHeaders Host rateLimitManager.ts: 1034 -> 945. The four items are pure (no limiter state, no external deps), so the leaf has ZERO imports — it cannot import the host (check:cycles clean). The host imports all four back (used by updateFromHeaders). They were module-internal, so the public API is unchanged (17 exports). Bodies moved byte-identical. Behavior-preserving: 21 existing rate-limit consumer tests stay green (rate-limit-manager 7, limiter-lifecycle 4, queue-timeout-msg 2, idle-eviction 6, body-lock 2); new tests/unit/ratelimitmanager-headers-split.test.ts (7 assertions) pins parseResetTime (durations / bare-number / nullish) + toPlainHeaders + guards the 17-function public API (with a watchdog-timer teardown hook so the runner exits cleanly). Refs #3501. * fix(config): back boot-hydrated proxy config singletons with globalThis (#5312) (#5742) Next.js compiles instrumentation.ts as a separate webpack module graph from the app-route/open-sse executors, so a module-local `let _config` is duplicated: the boot-time hydration (applyRuntimeSettings / restore hooks) lands on the instrumentation graph's copy, but the request path (base.ts) reads a different, un-hydrated copy. Live VPS validation proved the Thinking-Budget hydrate ran to completion at boot yet base.ts still read the passthrough default — why #5312 fix A stayed broken after the boot-wiring fix. Back the singletons with globalThis (the pattern systemPrompt.ts already uses for #2470) so all graph copies share one instance: - thinkingBudget.ts — dashboard Thinking-Budget mode reaches the executor - backgroundTaskDetector.ts — opt-in background degradation actually fires - systemTransforms.ts — operator pipeline overrides reach the request path payloadRules.ts was already safe (lazy per-request DB self-load, #2986). Guards: thinking-budget-globalthis-5312 + runtime-config-globalthis-5312 (assert globalThis sharing; a module-local let fails them, RED->GREEN). * refactor(evals): extract built-in golden-set suites from evalRunner.ts (#5740) Move the 7 static built-in eval suites (golden-set, coding-proficiency, reasoning-logic, multilingual, safety-guardrails, instruction-following, codex-comparison) plus the builtInSuites aggregate into the pure-data leaf src/lib/evals/evalRunner/builtinSuites.ts (zero imports, no side effects). evalRunner.ts keeps all logic (register/get/list/evaluate/run/scorecard/reset) and registers the leaf suites at module load, mirroring the original inline calls. Public API is unchanged (7 exported functions; the suite consts were already module-private). Host 960->301 LOC; leaf 676 LOC (< 800 cap); host was frozen-satisfied (961), so this is debt reduction. Suite data moved verbatim (652 data lines byte-identical). New split-guard test characterizes the suite ids/case counts/key cases and proves the host registers every leaf suite at load. * refactor(models): extract pure transform layer from modelsDevSync.ts (#5743) Move the models.dev data-model types, the provider-id mapping table (MODELS_DEV_PROVIDER_MAP + mapProviderId), and the raw->OmniRoute transforms (transformModelsDevToPricing, transformModelsDevToCapabilities) into the pure leaf src/lib/modelsDevSync/transform.ts (zero imports, no DB, no module state). modelsDevSync.ts keeps all sync orchestration, DB access, caches and the periodic-sync timer; it imports the transforms for internal use and re-exports mapProviderId/transformModelsDevToPricing/transformModelsDevToCapabilities plus the ModelCapabilityEntry/CapabilitiesByProvider types, so the public API is unchanged. Host 924->677 LOC; leaf 279 LOC (< 800 cap); host was frozen-satisfied (934), so this is debt reduction. 238 moved lines are byte-identical. New split-guard test characterizes the provider map + both transforms and proves the host re-exports them. * refactor(resilience): split settings.ts into types + normalize leaves (#5745) Decompose the (fully pure) resilience settings module into two sibling leaves: - src/lib/resilience/settings/types.ts: the settings shape (11 public interfaces + JsonRecord/AuthCategory), zero imports. - src/lib/resilience/settings/normalize.ts: the coercers (asRecord/toInteger/ toBoolean/feature-flag resolvers) + the 11 per-section normalize* functions. settings.ts keeps DEFAULT_RESILIENCE_SETTINGS, DEFAULT_REQUEST_QUEUE_MAX_WAIT_MS, buildLegacyFallback, and the public orchestrators (resolveResilienceSettings, mergeResilienceSettings, buildLegacyResilienceCompat); it imports the coercers/normalizers for internal use and re-exports the 11 settings interfaces, so the public API is unchanged. Host 840->363 LOC; leaves 182 + 359 LOC (< 800 cap); host was frozen-satisfied (841), so this is debt reduction. 472 moved lines are byte-identical; no cycles (leaves never import the host). New split-guard test characterizes the coercers/normalizers and the host resolve/merge/compat orchestration. * docs(readme): document faster/leaner install — skip native build, sql.js fallback (#5713) Documents the optional better-sqlite3 + pure-JS fallback chain and OMNIROUTE_SKIP_POSTINSTALL/CI skip flags. Docs-only, claims verified. (#5550) * feat(compression): T02 opt-in per-engine pipeline circuit-breaker (#5735) Opt-in, default-off per-engine circuit-breaker for the stacked compression pipeline. Byte-identical to legacy when off. 9 regression tests. * docs: sync MCP tool count to 95 + routing-strategy count (#5732) Sync CLAUDE.md/README.md to canonical MCP tool count (95, 35 base) and routing strategies (17). Numbers fact-checked against getAllToolDefinitions()/ROUTING_STRATEGY_VALUES. * feat(api): add first-class Ollama local provider card (#5712) First-class ollama-local provider card (localhost:11434/v1, keyless, passthrough models) in LOCAL_PROVIDERS + SELF_HOSTED + default.ts executor case. Docs count 236→237, Local 11→12 (full README sweep). 4 tests. (#5578) * feat(api): add opt-in API-key provider quota-policy bypass scope (#5731) Adds an opt-in per-API-key scope (policy:bypass-provider-quota) that lets a key skip provider/account-side quota cutoffs during routing. Operator USD budgets/usage limits still enforced unconditionally (fail-closed, before the bypass). Default-off; UI toggle + badge in API Manager. Integrated into release/v3.8.43. * feat(codex): opt-in auto-sync of Codex profiles after model discovery (#5737) Auto-sync ~/.codex/*.config.toml profiles after a provider model sync, reusing the setup-codex generator. Opt-in, default OFF (OMNIROUTE_AUTO_SYNC_CODEX_PROFILES=true; also honors CLI_ALLOW_CONFIG_WRITES). Never touches the active Codex config. Gating test added. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(providers): opt-in CLI profile auto-sync toggles + Claude Code auto-sync (#5755) Providers-dashboard 'CLI profile auto-sync' card (Codex + Claude Code toggles), feature-flag backed (default off), + Claude Code auto-sync mirroring the Codex path. Follow-up to #5737. * feat(compression): T08/H8 (2.3) — graduated CCR retrieval-feedback ramp (#5739) Turns CCR retrieval feedback from a binary cliff into a graduated ramp: each prior retrieval raises a block's effective minChars linearly (effectiveMinChars); >= 3 retrievals still excluded (Infinity). retrievalRampFactor default 2 (config/env COMPRESSION_CCR_RETRIEVAL_RAMP_FACTOR); 1 = legacy binary. Regression guard: tests/unit/compression/ccr-retrieval-ramp.test.ts (12); 51 existing CCR tests green. gaps v3.8.42 — T08/H8 (2.3). * feat(compression): T08/H5 (2.4) — usage-observed prefix freeze (opt-in) (#5744) Evolves the cache-aware guard to also learn which system prompts recur: observed >= threshold → treated as a stable cacheable prefix and preserved even for providers the static check misses. Content-addressed by a hash of the system prompt (OpenAI/Claude/Gemini), in-memory, freeze=preserve (never mutates). Opt-in/default-off (COMPRESSION_PREFIX_FREEZE_ENABLED); respects the never preserve-mode. New prefixFreeze.ts wired into resolveCacheAwareConfig. Regression guard: prefix-freeze.test.ts (10); 44 cache-aware tests green. gaps v3.8.42 — T08/H5 (2.4). * feat(compression): T08/H7 (2.5) — read-lifecycle engine (collapse superseded reads) (#5754) New opt-in, default-off read-lifecycle engine: collapses stale/superseded file-Read tool results (same path re-read OR modified later) to a stub, keeping the current Read intact. Anthropic + OpenAI tool shapes; conservative (known tool names, exact path, strictly-later); fail-open. Lossy → opt-in. Regression guard: read-lifecycle.test.ts (10); 41 registry/pipeline suites green. gaps v3.8.42 — T08/H7 (2.5). Completes Onda 2. * fix(sse): anti-thundering-herd guard tolerates numeric-epoch cooldowns (#5747) markAccountUnavailable's dedupe guard used a raw `new Date()` on rateLimitedUntil, which can hold a numeric-epoch string (e.g. the Antigravity full-quota path via setConnectionRateLimitUntil). That produced Invalid Date/NaN, so the guard never detected an already cooling connection — a second concurrent failure on the same connection overwrote a long quota-exhaustion cooldown with a much shorter fresh backoff cooldown, making the account selectable again far sooner than intended. Reuses the existing cooldownUntilMs normalizer (#3954) instead of a raw Date parse. * fix(chat): harden non-streaming SSE aggregation (#5746) * fix: repoint DashScope/Alibaba setup links to consoles (#5665) (#5762) * fix: point Quick Start step 1 to API Keys page, not Endpoint (#5695) (#5763) * fix: onboarding wizard saves providers with unsupported validation (#5692) (#5764) * docs(security): document full LOCAL_ONLY route set + GHSA-fhh6-4qxv-rpqj + audit path (#5599) (#5748) Expand ROUTE_GUARD_TIERS.md Tier 1 (LOCAL_ONLY): - link the GHSA advisory and explain the attack class (RCE via a subprocess spawn reachable from non-loopback traffic) - replace the 3-example prefix table with the full LOCAL_ONLY set, mirroring LOCAL_ONLY_API_PREFIXES / LOCAL_ONLY_API_PATTERNS in routeGuard.ts (the authoritative source; check-route-guard-membership enforces the code side) - add an "Operator guidance & auditing" section for users behind nginx/Cloudflare/Tailscale: don't forge X-Forwarded-For loopback, keep the manage-scope bypass minimal, and how to audit non-loopback access Docs-only; SECURITY.md already links here. Closes #5599 * docs(security): document banned-keyword / account-ban detection (#5600) (#5756) * docs(security): add BAN_DETECTION.md — banned-keyword / account-ban detection (#5600) New docs/security/BAN_DETECTION.md documenting the previously-undocumented system: - the 8 built-in ACCOUNT_DEACTIVATED_SIGNALS + custom keywords are additive - detection flow (body substring match -> terminal `banned` state, skipped in account selection; `deactivated` on 401/403; autoDisableBannedAccounts) - scope: global (all providers); the signal strings target OAuth/subscription scrapers - custom keywords: add path, 200-char cap, hot-reload, and the false-positive warning (raw substring match -> prefer full ban sentences, not "quota"/"limit") - recovery: terminal states never auto-recover -> re-test / re-auth / re-enable Registered in security meta.json; cross-linked from RESILIENCE_GUIDE (terminal states). Docs-only. Closes #5600 * docs(security): clarify deactivated vs expired terminal-status split (#5600) The same ACCOUNT_DEACTIVATED signal surfaces as two different terminal statuses depending on the code path: chatCore.ts inline writes 'deactivated' (401/403 via classifyProviderError), while markAccountUnavailable() -> resolveTerminalConnectionStatus() writes 'expired'. Document both. * fix: surface relay proxy-test errors instead of silent failure (#5716) (#5765) * refactor(api): extract pure discovery leaves from provider-models route (#5758) Split src/app/api/providers/[id]/models/route.ts (2511 -> 1818 LOC) by moving the cohesive, DB-free discovery building blocks into four leaves under discovery/: - helpers.ts record/string coercion, Azure + base-url helpers, bearer/named-openai header builders - normalizers.ts Antigravity / DataRobot / OpenAI-like / SAP models response normalizers - providerModelsConfig.ts PROVIDER_MODELS_CONFIG + ProviderModelsConfigEntry - providerSets.ts NAMED_OPENAI_STYLE_PROVIDERS + isNamedOpenAIStyleProvider The host keeps all request orchestration and imports the leaves back. The moved symbols were module-private, so the route's public export set (GET) is unchanged and no external importer needs updating. Bodies are byte-identical: the code-line multiset of host + leaves equals the original route verbatim. Tests: - repoint the qwen-web source-guard in catalog-updates-v3829-kimi-qwen to the new config leaf (assertions unchanged) - add provider-models-discovery-split as the split regression guard (leaf public surface + host wiring + the #5570 cablyai->aimlapi entry swap) * fix(memory): enabling Qdrant activates it as the engine + inline guidance (#5597) (#5741) * fix(memory): enabling Qdrant now activates it as the engine + inline guidance (#5597) Enabling Qdrant in the Engine tab was inert: retrieval only routes to Qdrant when memoryVectorStore === "qdrant" (the default "auto" never selects it), and the card only wrote qdrantEnabled — nothing set the engine selector, and there is no UI for it. So users configured Qdrant, saw "enabled", but it was never actually used. - PUT /api/settings/qdrant now sets memoryVectorStore alongside the toggle: enable -> "qdrant", disable -> "auto". Editing other fields leaves it untouched. - Add inline guidance to QdrantConfigCard: a Tier-1-vs-Tier-2 banner + per-field help (host, collection, embedding model). Note there is no "vector dimension" or "distance metric" field: dimension is auto-detected from the embedder, distance is always Cosine. - Document the real behavior in MEMORY.md: engine gate, no back-fill of existing memories, dimension auto-detect, Cosine-only, API-key-only auth. Tests: tests/integration/qdrant-routes.test.ts — enable->qdrant, disable->auto, and field-edit-without-enabled leaves the engine untouched (TDD: red -> green). Closes #5597 * fix(memory): invalidate memory-settings cache on Qdrant toggle (#5597) The PUT handler wrote memoryVectorStore to the DB but retrieval reads through getMemorySettings(), a module-level cache. Without busting it, the engine switch did not take effect until a process restart (the DB said qdrant, retrieval kept routing to sqlite-vec). Now calls invalidateMemorySettingsCache() after the write, mirroring src/app/api/settings/memory/route.ts. Regression test warms the cache, toggles via the route, and asserts getMemorySettings().vectorStore flips to qdrant (fails without the invalidate call). * fix(compression): record Context Editing telemetry on the streaming path (#5761) Streaming SSE responses now preserve context_management from the final message_delta snapshot and fire the telemetry hook in onStreamComplete, so context-clear savings surface in compression analytics for streaming (not just non-streaming). Additive telemetry, Claude-only, opt-in-neutral. gaps v3.8.42 — T01 (5.1). Test: context-editing-streaming-telemetry.test.ts (3, failing->passing). * Persist batch item checkpoints during recovery (#5753) * fix(sse): checkpoint batch item recovery * fix(db): renumber batch checkpoints migration 110→112 (collision with #5667) 110 was taken by 110_model_context_overrides.sql (#5667), which landed on the release branch after this PR branched. migrationRunner throws a hard version- collision error on startup when two files share a numeric prefix. 112 is the next free slot (110/111 taken on the release tip). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix: resolve CCR MCP retrieve principal from api-key auth context (#5649) (#5768) * feat(cli): show version in startup banner (integrates #5752) (#5769) * feat(cli): show version in startup banner Print dim 'v<version>' line below ASCII art logo in omniroute serve. Uses readFileSync (same pattern as program.mjs) to read package.json. Closes #5749. * test(cli): guard startup-banner version line (#5752) Source-inspection test (same pattern as cli-serve-port.test.ts) asserting serve.mjs parses the version from package.json and prints v${_pkg.version} in the startup banner — satisfies Hard Rule #8 for the bin/ change. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * docs(changelog): credit #5752 startup-banner version line (thanks @chirag127) --------- Co-authored-by: Chirag Singhal <76880977+chirag127@users.noreply.github.com> * fix(proxyfetch): skip fallback for non-replayable bodies (#5770) * chore(release): open v3.8.42 cycle Bump version to 3.8.42, add CHANGELOG placeholder, sync openapi/electron/open-sse + 42 i18n CHANGELOG mirrors. * chore: remove unused qdrant schema aliases (#5404) Integrated into release/v3.8.42 * chore: remove unused memory schema aliases (#5403) Integrated into release/v3.8.42 * chore: remove unused quota schema types (#5402) Integrated into release/v3.8.42 * chore: remove unused playground row type (#5401) Integrated into release/v3.8.42 * chore: remove unused codegraph exports (#5400) Integrated into release/v3.8.42 * chore: remove unused notion client type (#5399) Integrated into release/v3.8.42 * chore: remove unused settings types (#5398) Integrated into release/v3.8.42 * chore: remove unused combo types (#5396) Integrated into release/v3.8.42 * chore: remove unused provider types (#5393) Integrated into release/v3.8.42 * chore: remove unused skillssh skill type (#5392) Integrated into release/v3.8.42 * chore: remove unused status hex key type (#5391) Integrated into release/v3.8.42 * chore: remove unused batch provider type (#5390) Integrated into release/v3.8.42 * chore: remove unused skills schema types (#5389) Integrated into release/v3.8.42 * chore: remove unused codex auth input type (#5388) Integrated into release/v3.8.42 * chore: remove unused memory schema types (#5387) Integrated into release/v3.8.42 * chore: remove unused playground row type (#5386) Integrated into release/v3.8.42 * chore: remove unused qdrant schema types (#5385) Integrated into release/v3.8.42 * chore: remove unused kiro social schema (#5384) Integrated into release/v3.8.42 * chore: remove unused memory schema types (#5383) Integrated into release/v3.8.42 * chore: remove unused audit action type (#5382) Integrated into release/v3.8.42 * chore: remove unused agent skills schema types (#5381) Integrated into release/v3.8.42 * chore: remove unused shared logger default export (#5380) Integrated into release/v3.8.42 * chore: remove unused sse logger helpers (#5378) Integrated into release/v3.8.42 * chore: remove unused sse model legacy helpers (#5377) Integrated into release/v3.8.42 * chore: remove unused v1 search response schema (#5376) Integrated into release/v3.8.42 * chore: remove unused cloud agent result schemas (#5375) Integrated into release/v3.8.42 * chore: remove unused a2a routing logger readers (#5374) Integrated into release/v3.8.42 * chore: remove unused webhook delivery detail export (#5372) Integrated into release/v3.8.42 * chore: remove unused api key type (#5395) Integrated into release/v3.8.42 * chore: remove unused usage types (#5397) Integrated into release/v3.8.42 * chore: remove unused cloud agent input types (#5373) Integrated into release/v3.8.42 * deps: bump electron from 42.4.1 to 42.5.1 in /electron (#5413) Integrated into release/v3.8.42 * deps: bump the production group with 11 updates (#5414) Integrated into release/v3.8.42 * fix: frame non-streaming JSON responses (#5416) Integrated into release/v3.8.42 * fix(services): runNpm shell on win32 + prefix via env for Node 24 EINVAL (#5379) (#5474) Node 24 refuses execFile of npm.cmd without a shell (nodejs/node#52554), so embedded-service install (9Router/CLIProxy) failed with spawn EINVAL on Windows. runNpm now enables shell on win32 only; to stay Hard-Rule-#13 safe under a shell, the install --prefix is passed via npm_config_prefix (env) instead of an argv path (survives spaces), and the user-supplied version is constrained by SERVICE_VERSION_PATTERN at the route boundary. * fix(cli): restore dist/tls-options.mjs to npm tarball (#5452) (#5503) Closes #5452 * fix(dashboard): render onboarding wizard on /providers/new (#5427) (#5505) Closes #5427 * fix(db): EBUSY-safe database import on Windows (#5406) (#5507) Closes #5406 * chore: remove unused gamification streak exports (#5463) * chore: remove unused headroom log tail export (#5464) * chore(dead-code): remove unused prompt cache control helper (#5466) * chore(duplication): share vscode metadata helpers (#5471) * chore(duplication): share auth zip extractors (#5475) * chore(duplication): share vscode tokenized request helper (#5479) * chore(duplication): share quota strategy ranking helpers (#5482) * chore(duplication): share recharts donut card (#5484) * chore(duplication): share provider specific validation (#5485) * chore(duplication): share batch response formatter (#5488) * chore(duplication): share redis runtime helpers (#5490) * chore(duplication): share version manager request parsing (#5492) * chore(duplication): share media generation route helpers (#5493) * chore(duplication): share settings transform schemas (#5496) * chore(duplication): share relay stream finalizer (#5497) * chore(duplication): share machine id fallback (#5498) * chore(duplication): share node sqlite adapter (#5500) * fix: treat terminal stream cancels as complete (#5491) * fix post-merge ci regressions (#5467) * fix: gate claude adaptive thinking defaults (#5480) Co-authored-by: KooshaPari <koosha@example.com> * fix(fallback): normalize provider error rule headers (#5473) Co-authored-by: KooshaPari <koosha@example.com> * fix(rate-limit): normalize queue refresh settings (#5499) Co-authored-by: KooshaPari <koosha@example.com> * chore(ci): add npm fetch-retry + release-freeze protocol (Hard Rule #21) (#5506) - .npmrc: bump fetch-retries 2->5 with backoff so transient registry ECONNRESET during npm ci (electron-release, v3.8.41) retries instead of failing the job; applies repo-wide. - CLAUDE.md Hard Rule #21: release-freeze coordination marker (label release-freeze) that campaign workflows honor before merging into the active release branch, preventing the mid-release commit races that forced CHANGELOG re-reconciliation in v3.8.40/v3.8.41. * chore(duplication): share service install helpers (#5495) Share service install helpers; re-add SERVICE_VERSION_PATTERN regex to the shared schema (dropped in extraction, #5474) + tests rejecting malformed versions. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * chore(duplication): share proxy route handlers (#5472) Share proxy route handlers; add resolveProxyLookupResponse regression test (3 branches + custom whereUsed param name). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * chore(duplication): share combo builder model options (#5477) Share combo builder model options; add regression test locking custom-model source classification (manual->custom, api-sync->imported). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * chore(dead-code): ratchet dead code baseline (#5468) Ratchet dead-code baseline to the true measured value (310 -> 225) after the v3.8.42 dead-code + duplication wave. Measured by check-dead-code.mjs on the tip. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(dashboard): provider-add UX — i18n labels, surface import warning, default key name (#5511) * fix(dashboard): provider-add UX — real i18n labels, surface import warning, default key name (#5421 #5428 #5429 #5431 #5435) Three rough edges in the Add-API-Key / model-import flow, all from the provider-catalog audit: 1. Validation Model + Account ID form fields shipped untranslated i18n stub copy ('Validation Model Id Label', etc.) that rendered verbatim. Replaced with real copy in en.json. 2. Model import silently fell back to the cached/local catalog — the route returns a 'warning' field the import hook never read. New pure helper extractImportWarning surfaces it as a log line. 3. Required connection-name field defaulted to '' (let browser autofill inject garbage like 'wiw'); now defaults to 'main'. Regression guard: tests/unit/provider-add-ux-i18n-import-warning.test.ts. * fix(dashboard): compress AddApiKeyModal comment to keep file under frozen size cap * fix(providers): align Muse Spark (Meta AI) cookie copy to ecto_1_sess (#5449) (#5513) * fix(providers): align Muse Spark (Meta AI) cookie copy to ecto_1_sess (#5449) The default Meta AI session cookie migrated from the retired abra_sess to ecto_1_sess (META_AI_DEFAULT_COOKIE), but the provider form hint and one 401 auth-failure message still named abra_sess, telling users to paste a cookie that no longer exists. Both strings now name ecto_1_sess. Regression guard: tests/unit/muse-spark-cookie-copy-5449.test.ts. * chore: reconcile CHANGELOG with release (keep #5449 + #5511 bullets) * fix(providers): correct FriendliAI (serverless) + Novita (/openai/v1) endpoints (#5430 #5455) (#5515) * fix(providers): correct FriendliAI (serverless) and Novita (/openai/v1) endpoints (#5430 #5455) Both rejected valid keys, verified live with real provider keys: - FriendliAI baseUrl was /dedicated/v1/... which 403s a serverless flp_* token; switched to /serverless/v1/... + serverless modelsUrl. - Novita baseUrl was the legacy /v3/... with a typo'd model id ai-ai/... (both 404); switched to OpenAI-compat /openai/v1/... + meta-llama/llama-3.1-8b-instruct. Regression guard: tests/unit/provider-endpoints-friendliai-novita.test.ts. * chore: reconcile CHANGELOG with release (keep #5430/#5455 + prior bullets) * fix(providers): gate import for tool-only providers + sanitize Coze validation error (#5420 #5426) (#5522) #5420: the 'Import Models' button now hides for tool-only providers (web search / web fetch) via a capability check over resolved serviceKinds, not just the -search suffix — firecrawl/jina-reader (webFetch) no longer show an Import button that 400s. No LLM/media provider is affected. #5426: Coze key validation no longer leaks the raw upstream envelope ({code,msg,logId,from}) into the UI; the Coze error becomes a friendly message, scoped to provider === 'coze' so no other provider is affected. Regression guards: tests/unit/model-listing-capability-5420.test.ts, tests/unit/coze-validation-error-5426.test.ts. * fix(providers): correct LongCat free tier — GA LongCat-2.0, one-time 10M (KYC) (#5508) LongCat's preview ended and the Flash-* line was retired (2026-05-29); the API now exposes only the GA LongCat-2.0 (1M context, 128K output). The free tier is a ONE-TIME 10M-token grant unlocked after account signup + KYC verification — NOT a recurring daily/monthly allowance. The catalog still described the retired preview/Flash models and a recurring 150M / 5M-per-day budget; this corrects every reference. Config / code: - registry/longcat: model LongCat-2.0-Preview -> LongCat-2.0, name + comment reflect one-time 10M (KYC) and pay-as-you-go beyond it. - freeModelCatalog: longcat-2.0-preview (150M, recurring-daily) -> LongCat-2.0 (10M, freeType one-time-initial via creditTokens). - freeTierCatalog: drop longcat from the recurring-monthly budget map (one-time credits are excluded by that catalog's own rule). - regional.ts freeNote: one-time 10M after signup + KYC, not recurring. - providerCostData: longcat-flash-lite -> longcat-2.0 (pay-as-you-go 0.75/2.95 per 1M, 10M free quota). - validation probe model longcat -> LongCat-2.0. Tests: - free-tier-catalog: longcat now absent from FREE_TIER_BUDGETS; providerCount 22->21 (clean 21->20); documented total ~1.39B. - tierResolver: sample model flash-lite -> LongCat-2.0. Docs: - README, PROVIDERS-GUIDE, FREE-TIERS-GUIDE, FREE_TIERS: 50M/day Flash-Lite -> one-time 10M LongCat-2.0 (KYC); 'No auth' -> API key + KYC. - Regenerated PROVIDER_REFERENCE.md (picks up the new freeNote). typecheck:core clean; changed-file lint 0 errors; docs-sync PASS. * fix(providers): Bytez OpenAI-compat base URL + auth-only key validation (#5422) (#5528) Bytez IS OpenAI-compatible at .../models/v2/openai/v1, but the registry stored the bare .../models/v2 base, so validation's chat-probe hit .../models/v2/chat/completions -> 404 -> 'endpoint not supported'. Part A: registry baseUrl -> full OpenAI-compat chat path. Part B: a Bytez account only serves catalog-provisioned models, so chat-probe validation 404s even for valid keys. validateBytezProvider instead probes the auth-only GET .../models/v2/list/tasks (200=valid, 401/403=invalid). Verified live with a real key: list/tasks -> 200 (valid) / 401 (invalid). Regression guard: tests/unit/bytez-validation-5422.test.ts. * fix(providers): remove dead Phind provider + dedupe HuggingChat catalog listing (#5530) Integrated into release/v3.8.42 (round 3). Dead Phind removal + HuggingChat dedupe, verified complete. * fix: protect dynamic dashboard tests with CSRF (#5405) Integrated into release/v3.8.42 (round 3). Reworked CSRF (HMAC-signed synchronized token). * docs: clarify bifrost relay backend envs (#5520) Integrated into release/v3.8.42 (round 3). Doc-only: bifrost relay envs. * test(quota): guard Claude-Code identity version lockstep (Phase 2) (#5514) Integrated into release/v3.8.42 (round 3). Claude-Code identity version lockstep guard. * feat(compression): T02 — honest default-on pipeline inflation guard (H1) (#5527) Integrated into release/v3.8.42 (round 3). T02 pipeline inflation guard * feat(compression): T05/C2 — caveman dedup + ultra packs for de, fr, ja (#5529) Integrated into release/v3.8.42 (round 3). T05/C2 caveman packs de/fr/ja * feat(compression): T05/C6 — Chinese (zh / wenyan) caveman pack + detection (#5532) Integrated into release/v3.8.42 (round 3). T05/C6 zh/wenyan pack + detection * feat(compression): T07/R9 — gradle + dotnet RTK catalog filters (#5537) Integrated into release/v3.8.42 (round 3). T07/R9 RTK gradle+dotnet filters * refactor(dashboard): T11 — drop duplicate caveman on/off toggle from the compression settings tab (#5524) Integrated into release/v3.8.42 (round 3). T11 consolidate duplicate caveman controls; i18n'd the panel hint string (source key). * test relay routing fallback headers (#5526) Integrated into release/v3.8.42 (round 3). Relay fallback header extraction + tests (drift-shed: dependabot #5415 commit dropped). * fix(opencode-plugin): bump to 0.2.0 + auto-publish on release (#5363) - Bump @omniroute/opencode-plugin from 0.1.0 to 0.2.0 so CI publishes the accumulated fixes (auto combos, schema fields, debug logging) that were merged after the initial 0.1.0 publish on May 24. - Add auto-bump step in npm-publish.yml: detects if the plugin dir changed since the last release tag and auto-increments patch version, so the plugin never falls behind again on future releases. Co-authored-by: herjarsa <herjarsa@users.noreply.github.com> * [codex] add bifrost auto fallback cooldown (#5519) Integrated into release/v3.8.42 (round 3). Bifrost auto fallback cooldown; header reconciled with #5526 helper + env-doc. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix onboarding schema client import (#5525) Integrated into release/v3.8.42 (round 3). Browser-safe onboarding schema import (drift-shed: dependabot #5415 dropped). * docs: add relay backend strategy guide (#5547) Port #5533 relay strategy guide to release/v3.8.42 (doc-only). * fix(chatgpt-web): support GPT-5.5 Pro handoff (#5536) Integrated into release/v3.8.42 (round 3). GPT-5.5 Pro async stream_handoff support (drift-shed: dependabot #5415 dropped). * fix(providers): persist Configured filter across page reloads (#5510) Integrated into release/v3.8.42 (round 3). Persist Configured filter across reloads; extracted shouldSyncProviderDisplayMode race guard + TDD test (Closes #4059). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(mimocode): route per-account traffic through SOCKS5 proxy dispatchers (#5521) Integrated into release/v3.8.42 (round 3). Per-account SOCKS5 dispatcher routing — completes #3837's stored proxy config with the actual undici dispatcher layer. Rebased onto .42 (dropped the CI-workflow-deletion commits; merged proxyUrlMap dispatch with #3837's acct.proxy storage). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(chatgpt-web): portable SHA3-512 for sentinel PoW under Electron/BoringSSL (#5531) (#5540) * fix(build): keep ioredis out of the client/CLI bundle via SPAWN_CAPABLE_PREFIXES leaf (#5546) Fix the dast-smoke ioredis client-bundle regression (proven: dast-smoke green). Remaining reds are pre-existing base-reds/flakes (base.ts file-size, GOLDEN provider drift, shard-1 compression flakes) inherited by all PRs — not from this change. * chore(release): finalize v3.8.42 CHANGELOG + cycle-close reconciliation - Reconcile CHANGELOG.md for v3.8.42: 40 bullets covering all 89 commits since v3.8.41 (4 features, 26 fixes, 10 maintenance incl. 2 rollups for the 35-PR dead-code sweep + 17-PR DRY consolidation), dedup the merge- artifact duplicate New Features headers, set release date 2026-06-30. - Sync 42 docs/i18n/*/CHANGELOG.md mirrors. - Document 3 new chatgpt-web/TLS env vars in .env.example + ENVIRONMENT.md (OMNIROUTE_CGPT_WEB_PRO_TIMEOUT_MS, _PRO_POLL_INTERVAL_MS, OMNIROUTE_CHATGPT_STREAM_FIRST_BYTE_TIMEOUT_MS). - Cycle-close ratchet rebaselines: eslintWarnings 4116->4121, file-size base.ts/chatgpt-web.ts/strategySelector.ts/chatgpt-web.test.ts (all inherited drift, justified inline). - Regenerate provider translate-path golden snapshot for the merged bytez/friendliai/novita endpoint fixes. * chore(changelog): cover #5415 dev-deps bump merged from main The release/v3.8.42 ↔ main merge ( |
||
|
|
78f09c8d9f |
Release v3.8.41 (#5327)
Release v3.8.41 — 52 commits since v3.8.40 (19 CHANGELOG bullets, 11 contributors). All gating CI green: Unit×8, Coverage×8, Vitest, Package Artifact, Quality Ratchet, CodeQL, Lint, Docs Sync (Strict), Node 24/26 compat, E2E×9, Integration, Electron smoke. Advisory checks overridden (main unprotected): PR Test Policy = test-masking heuristic on the cumulative 52-commit assert delta (legitimate dead-code-sweep removals + consolidations, reviewed per-PR); SonarCloud/SonarQube = new-code maintainability/coverage quality gate (CodeQL/Semgrep/Security/npm-audit/Dependabot all clean — not a security finding). |
||
|
|
a7ae9550bd |
Release v3.8.36 (#4854)
* chore(release): open v3.8.36 development cycle * refactor(chatCore): extrai resolveCompressionSettings (#3501) (#4826) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 1/13) * refactor(chatCore): extrai predicados puros de combo de compressão (#3501) (#4824) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 2/13) * refactor(chatCore): extrai emitOutputStyleTelemetry (#3501) (#4811) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 3/13) * refactor(chatCore): extrai writeCompressionAnalytics (bloco analytics completo, #3501) (#4817) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 4/13) * refactor(chatCore): extrai runPluginOnRequestHook (#3501) (#4827) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 5/13) * refactor(chatCore): extrai applyClientUsageBuffer (buffer/estimate de usage non-streaming, #3501) (#4832) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 6/13) * refactor(chatCore): extrai buildPostCallGuardrailContext (contexto guardrail post-call, #3501) (#4831) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 7/13) * refactor(chatCore): extrai storeSemanticCacheResponse (cache-store non-streaming, #3501) (#4828) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 8/13) * refactor(chatCore): extrai buildNonStreamingResponseHeaders (headers de resposta non-streaming, #3501) (#4835) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 9/13) * refactor(chatCore): extrai maybeConvertJsonBodyToSse (#3089 JSON→SSE streaming, #3501) (#4833) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 10/13) * refactor(chatCore): extrai assembleStreamingResponseHeaders (headers de resposta streaming, #3501) (#4836) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 11/13) * refactor(chatCore): extrai storeStreamingSemanticCacheResponse (cache-store streaming, #3501) (#4829) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 12/13) * refactor(chatCore): extrai assembleStreamingPipeline (chain de transforms streaming, #3501) (#4837) Integrated into release/v3.8.36 (#3501 chatCore extraction stack 13/13) * ci(quality): shift heavy validations to the PR→release fast-path (release-acceleration) (#4857) * feat(quality): add check:test-runner-api gate (vitest-only dirs must use vitest API) * feat(release): reusable CHANGELOG i18n-mirror sync script * chore(ops): add prune-stale-worktrees.sh (dry-run by default) * ci(quality): run test-runner-api + docs-all + vitest + full unit suite on PR->release fast-path --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(quota): cota exclusiva lista qtSd/ no /v1/models (#4806) + limite EPSILON não bloqueia (#4830) Integrated into release/v3.8.36 — quota-exclusive qtSd/ listing (#4806) + EPSILON placeholder no longer blocks; rebuilt from stale base (3 defining commits cherry-picked clean over release tip) * feat(sse): add Google Flow video-generation provider (#4569) (#4769) Integrated into release/v3.8.36 — Google Flow video-generation provider (#4569), release-green validated (typecheck + 21 tests + file-size) * fix(api): auth on compression run-telemetry + document OMNIROUTE_EVAL_CREDENTIALS (#4694, #4720) (#4796) Integrated into release/v3.8.36 — auth on compression run-telemetry + OMNIROUTE_EVAL_CREDENTIALS doc, release-green validated (typecheck + 3 tests + env-doc-sync) * fix(translator): strip top-level client_metadata on the OpenAI passthrough (port from 9router#1157) (#4624) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(translator): normalize `developer` role to `system` for OpenAI-format providers (#4625) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(translator): emit </think> close marker for Anthropic thinking blocks (#4633) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(translator): normalize tools to Anthropic-native shape for non-Anthropic providers (#4650) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(gemini): preserve `pattern` in antigravity tool schema sanitizer (#4651) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(perplexity): validate API keys via /v1/models endpoint (#4654) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(image): prevent compatible nodes from shadowing provider aliases (#4656) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(cli-tools): tolerate JSONC (comments, trailing commas) in tool settings (#4659) Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) * fix(security): validate kiro region to prevent SSRF (GHSA-6mwv-4mrm-5p3m) (#4629) Integrated into release/v3.8.36 — kiro region SSRF guard (GHSA-6mwv-4mrm-5p3m), port rebuilt clean over release tip * fix(cli): harden the systray2 tray runtime (port of 9router#1080) (#4628) Integrated into release/v3.8.36 — port rebuilt clean over release tip, release-green validated * fix(test): validate anthropic-compatible connections via POST /v1/messages (#4657) Integrated into release/v3.8.36 — anthropic-compat validation via POST /v1/messages (port 584cf66a), rebuilt clean + baseline; release-green * fix(executors): strip params unsupported by the target provider/model (#4658) Integrated into release/v3.8.36 — port rebuilt clean over release tip, release-green validated * fix(claude-oauth): respect 429 backoff on usage endpoint to reduce spam (#4655) Integrated into release/v3.8.36 — port rebuilt clean over release tip, release-green validated * feat(api/v1): include alias-backed models in /v1/models listing (#4630) Integrated into release/v3.8.36 — port rebuilt clean over release tip, release-green validated * chore(quality): rebaseline catalog.ts 1574->1577 (#4630 aliases sobre quota-exclusive da release) (#4879) rebaseline * feat(compression): Kiro/CodeWhisperer tool-result compression engine (#4635) Integrated into release/v3.8.36 — port rebuilt clean, release-green * fix(security): don't trust loopback socket as local when behind reverse proxy (#4632) Integrated into release/v3.8.36 — port rebuilt clean, release-green * fix(opencode): preserve DeepSeek reasoning content in streamed responses (#4631) Integrated into release/v3.8.36 — DeepSeek reasoning_content injection (port #1099); release-green * fix(copilot,antigravity): cap maxOutputTokens at 16384 to stop "Invalid Argument" 400 (#4636) Integrated into release/v3.8.36 — cap maxOutputTokens 16384 antigravity (port #779); release-green * fix(dashboard): show custom vision models in LLM selector (#4653) Integrated into release/v3.8.36 — custom vision models in LLM selector (port 5e5e78d3); release-green * fix(claude): omit adaptive thinking + output_config.effort for haiku (#4661) Integrated into release/v3.8.36 — haiku adaptive-thinking omit (port); release-green * feat(provider): CodeBuddy CN (copilot.tencent.com) — full stack (#4664) Integrated into release/v3.8.36 — CodeBuddy CN provider (port efd20be8); usage.ts import + public-creds allowlist line reconciled; release-green * feat(combo): Fusion strategy — parallel panel + judge synthesis (16th strategy) (#4652) Integrated into release/v3.8.36 — Fusion combo strategy (16th, port 87e5c1c6); combo.ts baseline reconciled; release-green * feat(proxy-pool): Deno Deploy relays + group action buttons (#4643) Integrated into release/v3.8.36 — Deno Deploy relays (port #1437); proxies.ts baseline reconciled + env docs restored; release-green * fix(security): pin image fetch DNS resolution to prevent SSRF rebinding (GHSA-cmhj-wh2f-9cgx) (#4634) Integrated into release/v3.8.36 — pin DNS for image fetch SSRF rebinding guard (GHSA-cmhj-wh2f-9cgx, port c7d07448); caller DNS stubs + test-file baseline reconciled; release-green * fix(github): route Copilot Codex models to /responses (port from 9router#102) (#4626) Integrated into release/v3.8.36 — route Copilot Codex models to /responses (port #102); release-green * fix(copilot): never route Gemini/Claude variants to /responses (chat-completions only) (#4627) Integrated into release/v3.8.36 — never route Gemini/Claude to /responses (port #1536); fused with #4626 codex routing via supportsResponsesEndpoint gate; release-green * docs(ops): add canonical incident response runbook (#4868) Integrated into release/v3.8.36 * docs(perf): add per-endpoint p50/p95/p99 latency + cost budgets (#4867) Integrated into release/v3.8.36 * fix(proxy): fan out direct dispatcher streams (#4803) Integrated into release/v3.8.36 * fix(antigravity): exclude standard Gemini rate limit message from quota exhaustion keywords (#4810) Integrated into release/v3.8.36 * fix(sse): skip third-party tool-name cloak for Anthropic server tools (#4808) Integrated into release/v3.8.36 * fix(install): make transformers optional for CUDA-host installs (#4807) Integrated into release/v3.8.36 * fix(combo): propagate selected connection ID to fallback error responses for correct model lockout (#4809) Integrated into release/v3.8.36 * fix db storage tuning settings (#4834) Integrated into release/v3.8.36 * fix(sse): drop ccp pin when pinned provider is durably unhealthy (failover + anti-flap) (#4864) Integrated into release/v3.8.36 * fix(claude): skip mcp__ tool-name cloak + guard missing connectionId (#4861) Integrated into release/v3.8.36 * chore(quality): reconcile env-doc + file-size base-reds in release/v3.8.36 (#4886) - env-doc-sync: document PIN_DROP_BACKOFF_LEVEL / PIN_DROP_GRACE_MS (added by the ccp-pin health gate #4864) in .env.example + ENVIRONMENT.md. - file-size: rebaseline image-generation-handler.test.ts 1996 -> 2019 to its actual size (pre-existing drift). Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(codex): drop non-standard codex.* events that break responses.stream (env-gated, #4602) (#4715) Integrated into release/v3.8.36 * feat(routing): honor X-Route-Model header to override body.model (#4863) Integrated into release/v3.8.36 * feat(live-ws): allow non-loopback clients via LIVE_WS_ALLOWED_HOSTS (closes #4873) (#4877) Integrated into release/v3.8.36 (live-ws + combo-api commits; Tailscale CGNAT commit held pending opt-in/opt-out decision) * chore(claude,codex): bump pinned CLI identity — Claude 2.1.158→2.1.187, Codex 0.132.0→0.142.0 (#4883) Integrated into release/v3.8.36 * fix(security): SSRF allowlist bypass via x-relay-path nos relays Deno/Vercel (#4899) Integrated into release/v3.8.36 * feat(quota): recuperação proativa de conexões em cooldown (cron heal) [Fase 3 #8] (#4900) Integrated into release/v3.8.36 * fix(quota): policy inválida não vaza allow + guard connectionIds vazio [Fase 3 #10] (#4901) Integrated into release/v3.8.36 * feat(quota): saturação real do Claude no fair-share via /api/oauth/usage (#4885) Integrated into release/v3.8.36 * chore(dashboard): rename Qoder display label from "Qoder AI" to "Qoder" (#4733) Integrated into release/v3.8.36 * fix(ci): include coverage/lcov.info in coverage-report artifact for SonarQube (#4670) Integrated into release/v3.8.36 * fix(cli): bump better-sqlite3 runtime pin to 12.10.1 for Node 26 (#4685) Integrated into release/v3.8.36 * docs: clarify Kiro is ~50 credits/month per account, not unlimited (#4690) Integrated into release/v3.8.36 * docs(agentbridge): document Electron NODE_EXTRA_CA_CERTS, real model IDs, identity caveat (#4718) Integrated into release/v3.8.36 * docs(ops): document the release-green family (green-prs, check:release-green, babysit, nightly) (#4679) Integrated into release/v3.8.36 * fix(translator): replay reasoning_content on plain Xiaomi MiMo turns (port from 9router#1321) (#4639) Integrated into release/v3.8.36 * feat(opencode-go): advertise glm-5.2 and kimi-k2.7-code (align with official Go endpoints) (#4711) Integrated into release/v3.8.36 * feat(db): track API endpoint dimension on usage_history (#4676) Integrated into release/v3.8.36 (migration renumbered 103→105; endpoint plumbed through extracted usage-stats helpers) * fix(cli): SIGKILL systray child PID before IPC close to avoid macOS NSStatusItem orphan (#4732) Integrated into release/v3.8.36 * feat(proxy-pool): Cloudflare Workers proxy deployer + pool integration (#4640) Integrated into release/v3.8.36 (relay type added to RELAY_TYPES set; dropdown UX preserved + Cloudflare item added; proxies.ts file-size rebaselined 1057→1060) * chore(quality): conserta base-red de release/v3.8.36 (gates + 7 testes + build MDX) (#4915) A base tinha base-red sistêmica herdada de PRs de outras sessões, bloqueando TODOS os PRs do ciclo (o TIA roda a suíte full em fail-safe p/ diffs hub). 4 Fast Quality Gates: - test-discovery (#4877): live-server-allowlist.test.ts em tests/unit/server/ (não-coletado) + vitest → nunca rodava. Convertido p/ node:test em tests/unit/security/. - any-budget:t11 (#4664): 3 explicit-any em tokenRefresh.ts tipados (sem crescer file-size). - docs-symbols (#4868): rotas inexistentes → /api/system/version e PUT /api/providers/{id} {isActive:false}. - docs-all fabricated-claim (#4868 + #4718): 5 bin/*.sh reais criados (rollback, snapshot-data, restore-data, restore-policies, cold-start-bench) + _ops-common.sh (snapshot VACUUM INTO, guards de confirmação/TTY, testes de contrato); NODE_EXTRA_CA_CERTS (env de runtime Node) na allowlist do checker. 7 testes unit base-red (de features alheias à quota): - oauth-providers-config (#4664): teste alinhado ao provider codebuddy-cn do registry. - antigravity-model-aliases (#4636): maxOutputTokens esperado 32769→16384 (cap intencional). - provider-request-capture #4091 (#4861): exemplo do teste trocado de mcp__ (que #4861 isenta de cloak por causa dos 400s de assimetria de histórico) para um tool de terceiro cloakável — preserva o invariante de #4091 SEM reverter #4861. - combo-error-response: convertido de vitest p/ node:test (era coletado pelo glob node:test e crashava); api/** e server/** removidos do vitest.config (config morta). Build MDX (dast-smoke, #4679): - docs/ops/RELEASE_GREEN.md não tinha frontmatter `title` → fumadocs-mdx rejeitava no webpack compile ("invalid frontmatter: title expected string"), quebrando o next build (e o deploy). Frontmatter title adicionado (único doc do collection sem ele). 17/17 Fast Quality Gates + suíte unit completa (17737 testes, 0 fail) + vitest verdes localmente. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(quota): saturação proativa por headers de tokens (universal) [Fase 3 #2] (#4907) storeRateLimitHeaders só capturava os headers de REQUESTS (RPM/min), que não refletem a pressão de TOKENS. Agora também parseia os headers de tokens (em toda resposta, sucesso também) para throttle proativo antes do 429: - Anthropic: anthropic-ratelimit-tokens-{limit,remaining,reset} (+ input/output), RFC3339. - OpenAI: x-ratelimit-{limit,remaining,reset}-tokens, reset em duração (6m0s). saturation = 1 − remaining/limit; resetAt normalizado a epoch (parse de duração ReDoS-safe). getTokenHeaderSaturation por (provider, connectionId). fetchGeneric- Saturation passa a usar esse sinal (complementa o oauth/usage do #1, que segue primário p/ Claude). Fail-open, cache mantido, request-path inalterado. 16 testes novos + regressão (oauth/usage #1 8/8, signals 6/6) = 30/30; typecheck:core + eslint limpos. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(quota): estratégia de combo "headroom" — seleção por folga de cota [Fase 3 #4] (#4908) Nova estratégia de roteamento que escolhe a conexão com MAIS folga de plano: headroom = 1 − max(util_5h, util_7d) (técnica do dario), via getSaturation (melhorado p/ Claude no #1). Proativo em vez de só fill-first reativo. - Helper PURO headroomRanking.ts (computeHeadroom + rankByHeadroom; saturação injetada, não-mutante, tie-break estável, fail-open). - Orderer async em combo/quotaStrategies.ts (reusa a maquinaria reset-aware de expansão de conexões + concorrência limitada; seam injetável). - Registrada como "headroom" em routingStrategies (combo-only); fill-first segue default — nenhuma estratégia existente tocada. - baseline file-size combo.ts 3168->3180 (só +12L de dispatch; lógica fora do god-file). 16 testes novos + combo-strategies 15/15 = 31/31; typecheck:core + eslint + file-size limpos. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(quota): cap per-(key,model) — quota_allocation_model_caps [Fase 3 #7] (#4927) * feat(quota): cap per-(key,model) com tabela quota_allocation_model_caps [Fase 3 #7] Fecha o buraco onde uma API key pode drenar o pool inteiro consumindo um único modelo. Tabela nova: quota_allocation_model_caps(pool_id, api_key_id, model, cap_value, cap_unit) PK composta (pool_id, api_key_id, model). cap_unit alinhado ao QuotaUnit existente. Comportamento: keyA acima do cap para modelo M → bloqueada somente em M; ainda permitida em qualquer outro modelo no mesmo pool. Cap <= EPSILON → ignorado (seed). Consumo por-(key,model) usa bucket segregado no quota_consumption existente (poolId mangled ':model:<model>') com window fixa 'hourly'; nenhuma nova tabela ou método de store necessário. Módulo novo: src/lib/db/quotaModelCaps.ts (getModelCap/setModelCap/deleteModelCap/listModelCaps) enforce.ts ganha o pre-check em enforceQuotaShare + recording em recordConsumption. EnforceInput e RecordConsumptionInput ganham model?: string (backward-compatible). localDb.ts re-exporta os 4 helpers (Hard Rule #2). TDD: tests/unit/quota-per-key-model.test.ts — 4 cenários (bloqueia em M, permite em M2, sem cap → sem bloqueio, EPSILON → ignorado). Todos os gates de qualidade passam. * feat(quota): plumba model resolvido no hot path para ativar o per-(key,model) cap [Fase 3 #7] A tabela/enforce do commit anterior estavam INERTES: o hot path não passava `model` ao enforce nem ao record, então nenhum model-cap disparava em produção. Plumbagem (model resolvido = mesma var usada no log/roteamento, pós background-redirect/alias): - chatCore.ts: enforceQuotaShare ganha `model`; scheduleQuotaShareConsumption recebe `model`. - chatCore/quotaShareConsumption.ts: threade `model` no RecordConsumptionInput (non-streaming). - spendRecorder.ts: recordStreamingConsumption já recebia `model` — agora o coloca no RecordConsumptionInput (streaming accrue por-modelo). - embeddings.ts: enforce + record ganham `model`. Namespace do cap = id do modelo RESOLVIDO (o mesmo de modelForScope/pendingScope/getUnsupportedParams), não o requestedModel cru nem o finalModelToUpstream (sem prefixo de provider). Operador configura o cap contra esse id. `model || undefined` em todos os pontos: vazio/null → check pulado (fail-safe, zero latência — só um campo no objeto). Teste de integração novo (tests/unit/quota-per-key-model-hotpath.test.ts): prova end-to-end que N consumos via scheduleQuotaShareConsumption({model}) → enforceQuotaShare({model}) bloqueia, e que outro modelo no mesmo pool ainda passa; + guard de que enforce SEM model nunca dispara model-cap. --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(quota): session stickiness p/ integridade de prompt-cache [Fase 3 #5] (#4929) * feat(quota): session stickiness p/ integridade de prompt-cache [Fase 3 #5] Adiciona stickiness de sessão ao roteamento de combo: uma conversa multi-turno é roteada para a MESMA conexão enquanto ela permanecer saudável, evitando a perda do prompt-cache do provider (custo 5-10× sem stickiness, efeito conhecido no dario/clewdr). Implementação: - `open-sse/services/combo/sessionStickiness.ts` (novo, <800 linhas): mapa em memória (messageHash → connectionId) com TTL 15 min + cap 500 entradas; `applySessionStickiness` promove a conexão sticky ao índice 0 dos targets ordenados pelo strategy, guardado por `computeHeadroom > 0.15` (threshold); quando saturada (headroom ≤ 0.15), o binding é limpo e a seleção normal reage. Hash da sessão = SHA-256 dos primeiros chars da 1ª mensagem user → 16 hex chars. Seam de teste: `__setStickinessHeadroomFetcherForTests`. - `open-sse/services/combo.ts`: import + 2 pontos de integração (pré-eval-scores e pós-success), dentro do orçamento congelado de 3180 linhas. - `tests/unit/combo-session-stickiness.test.ts`: 19 testes node:test + assert/strict, todos via injeção de fetcher (zero rede/DB). Threshold 0.15: conexão a >85% de utilização está a um burst de rate-limit; o benefício de cache não compensa manter-se numa conexão degradada. Valor alinhado com a zona de soft-penalty do restante do engine de quota-share. * test(combo): isola combo-strategies da session stickiness (#5) selectedConnectionFor reusa o mesmo body, então o sticky map (#5) fixava a connection após a 1ª chamada e quebrava o round-robin tie-break do teste reset-aware. Limpa o sticky map no início da helper — a stickiness tem suíte própria (combo-session-stickiness). Sem enfraquecer asserts. --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(quota): buckets multi-janela por conexão (5h/7d/per-model) [Fase 3 #3] (#4928) Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * refactor(providers): decompõe catálogo providers.ts em módulos de dados (godfile sweep, #3501) (#4917) Integrado em release/v3.8.36 (godfile sweep providers.ts, #3501) * refactor(pricing): decompõe pricing.ts em shared-tiers + DEFAULT_PRICING particionado (godfile sweep, #3501) (#4918) Integrado em release/v3.8.36 (godfile sweep pricing.ts, #3501) * refactor(api): extrai camada-folha pura de validation.ts (URL/headers/transport) (#4921) Integrado em release/v3.8.36 (validation.ts split fatia 1 — leaf layer) * refactor(api): extrai validators web-cookie + Meta AI de validation.ts (#4922) Integrado em release/v3.8.36 (validation.ts split fatia 2 — web-cookie + Meta AI) * refactor(api): extrai validators enterprise-cloud + probe compartilhado de validation.ts (#4923) Integrado em release/v3.8.36 (validation.ts split fatia 3 — enterprise-cloud + probe) * refactor(api): extrai validators áudio/speech + misc apikey de validation.ts (#4930) Integrado em release/v3.8.36 (validation.ts split fatia 4 — áudio/speech + misc apikey) * feat(quota): estratégia dedicada de quota-share (DRR + P2C in-flight + gating per-model) [Fase 3 #9] (#4939) * feat(quota): estratégia dedicada de quota-share (DRR + P2C in-flight + gating per-model) [Fase 3 #9] Estratégia interna "quota-share" isolada num módulo dedicado — NÃO toca a seleção/ fair-share genérica (decisão do dono: não mexer no que já funciona). Os combos qtSd/ (quotaCombos.ts) passam de fill-first para essa strategy; combo.ts ganha só 1 branch de dispatch que delega 100% ao módulo (nenhum case existente alterado). - quotaShareStrategy.ts: gating per-model (isBucketSaturated do #3) + DRR (quantum proporcional ao weight) + P2C sobre carga in-flight. - quotaShareInflight.ts: contador in-flight com TTL/lease de 120s — fallback do decrement-on-abort sem precisar instrumentar o combo genérico. - "quota-share" registrada como strategy INTERNA (não exposta na UI). - testes de síntese (quota-combo-balancing, quota-multiprovider) alinhados: a strategy esperada dos combos qtSd/ passa de "fill-first" para "quota-share" (alinhamento ao novo comportamento intencional, não mascaramento — os 73 testes de qtSd/ seguem verdes). * test(quota-share): alinha 2 scope-guards ao godfile sweep (base-reds que bloqueavam o CI) Dois testes de "arquivo contém X" quebraram por decomposições de godfile que outras sessões mergearam no release DURANTE a validação de #9 — NÃO são regressão de #9 (que não toca validation/oauth). Alinhados ao novo layout, asserts preservados: - proxy-bypass-scope-guard #3226: bypassProxyPatch foi extraído de validation.ts para validation/headers.ts (split #4921–#4930) → o teste lê a camada de validação. - sse-error-passthrough #3324: a windsurf authHint foi extraída de providers.ts para providers/oauth.ts → o teste lê o novo local. --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * refactor(api): extrai validators search + embedding/rerank de validation.ts (#4932) Integrated into release/v3.8.36 * refactor(api): extrai format-validators (OpenAI/Anthropic) de validation.ts (#4933) Integrated into release/v3.8.36 * refactor(db): extrai model-permission matching de db/apiKeys.ts (#4936) Integrated into release/v3.8.36 * refactor(db): extrai row-parsers + tipos compartilhados de db/apiKeys.ts (#4943) Integrated into release/v3.8.36 * refactor(db): extrai column-mapping (snake↔camel) de db/core.ts (#4947) Integrated into release/v3.8.36 * refactor(db): extrai schema-column reconciliation de db/core.ts (#4948) Integrated into release/v3.8.36 * refactor(sse): extrai scalar/format helpers de services/usage.ts (#4949) Integrated into release/v3.8.36 * refactor(sse): extrai quota-core (UsageQuota + builders) de services/usage.ts (#4950) Integrated into release/v3.8.36 * fix(translator): regroup parallel tool results adjacent to their assistant (#4714) (#4882) Integrated into release/v3.8.36 (fixes #4714) * fix(qoder): exchange PAT for jt-* job token before Cosy chat (#4683) (#4884) Integrated into release/v3.8.36 (fixes #4683) * refactor(sse): dedup fallback tool_call id helper (#4736) Integrated into release/v3.8.36 * refactor(open-sse): extract safeParseJSON util, dedup tryParseJSON (#4735) Integrated into release/v3.8.36 * fix(compression): eliminate ReDoS in math_inline preservation pattern (#4795) (#4838) Integrated into release/v3.8.36 (fixes #4795) * fix(combo): fetch models dynamically from custom provider endpoints (#4860) Integrated into release/v3.8.36 * feat(providers): update volcengine-ark model list with DeepSeek V4 (#4905) Integrated into release/v3.8.36 * fix(translator): provider thinking compatibility (DeepSeek/Gemini) (#4946) Integrated into release/v3.8.36 * feat(combo): task-aware routing strategy (#4945) Integrated into release/v3.8.36 * refactor(sse): extrai a família MiniMax de services/usage.ts (#4952) Integrated into release/v3.8.36 * refactor(sse): extrai a família GLM de services/usage.ts (#4953) Integrated into release/v3.8.36 * refactor(sse): extrai a família Antigravity de services/usage.ts (#4956) Integrated into release/v3.8.36 * fix(dashboard): show custom provider given-name instead of internal id across dashboard pages (#4603) (#4960) Integrated into release/v3.8.36 (fixes #4603) * fix(api): evict stale in-memory rate-limit windows to stop slow heap leak (#4041) (#4957) Integrated into release/v3.8.36 (fixes #4041) * fix(api): parse /v1/responses body once instead of 3-4x on the hot path (#4041) (#4958) Integrated into release/v3.8.36 (fixes #4041) * fix(translator): preserve legitimate empty-string tool arguments in openai-to-claude streaming (#4951) (#4959) Integrated into release/v3.8.36 (fixes #4951) * chore(quality): reconcile file-size baseline for #4960 provider-display-name (#4961) Integrated into release/v3.8.36 * fix(dashboard): restore home provider-topology card hidden by #4596 default (#4963) Integrated into release/v3.8.36 — restores home topology card (#4596 regression) * fix(build): drop @omniroute/open-sse from optimizePackageImports (build OOM) (#4968) Integrated into release/v3.8.36 — fixes build OOM (optimizePackageImports open-sse) * fix(quota): migração 107 ativa estratégia quota-share nos combos qtSd/ existentes [Fase 3 #9] (#4962) Integrated into release/v3.8.36 * feat(quota): respeita max_concurrent por conexão no roteamento (#4965) Integrated into release/v3.8.36 * feat(quota): combo quota-share espera cooldown curto e re-despacha (Variante A) (#4967) Integrated into release/v3.8.36 * fix(quality): resolve base-reds da release — db-rules allowlist + task-aware router precedence (#4973) Dois base-reds pré-existentes que reprovavam o CI da release v3.8.36 (Fast Quality Gates + Unit Tests fast-path), independentes de qualquer feature em voo: 1. check:db-rules / allowlist: os módulos db-internal caseMapping (#4947) e schemaColumns (#4948), extraídos de db/core.ts e importados só por ele, não estavam em INTENTIONALLY_INTERNAL. Registrados na allowlist (correção canônica — são internos legítimos, não re-exportados pelo localDb). 2. auto-strategy honra LKGP/cost (combo-routing-engine.test.ts, 2 testes): o task-aware reordering (#4945, reorderByTaskWeight) roda para strategy "auto" e era aplicado DEPOIS do router explícito (selectWithStrategy: lkgp/cost), sobrescrevendo o orderedTargets[0] que o operador escolheu. Instrumentação provou: post-filter [0]=claude (LKGP) → post-task [0]=gpt-oss. Correção: quando o auto usa router explícito, preserva o [0] dele e deixa o task-aware refinar só a cauda de fallback. gpt-oss-120b PERMANECE tool-capable (não é mudança de catálogo; o model-capabilities-registry test segue verde). Validado: 121 testes (combo-routing-engine + combo-task-aware + registry) verdes, red-check confirmado, db-rules/file-size/typecheck/lint/prettier OK. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(quota): serializa concorrência por conexão no caminho quota-share (FASE 2.1) (#4970) O gating de quota-share em selectQuotaShareTarget é fail-open: uma conexão at-cap só é despriorizada, nunca bloqueada. Com 1 conexão por conta de assinatura (caso comum), chamadas concorrentes ainda floodam a conta (→ 429 + cooldown) — provado live na .15: 3 chamadas concorrentes com max_concurrent=1 despacharam todas em 94ms. Adiciona um semáforo POR CONEXÃO em torno do dispatch quota-share: chamadas excedentes esperam na fila em vez de floodar (key qsconn:<connectionId>, cap = max_concurrent da conexão). Fail-open em fila saturada/timeout para nunca piorar disponibilidade. Gated por strategy===quota-share + kill-switch resilienceSettings.quotaShareConcurrencyLimit (default on; UI no ResilienceTab). Lógica extraível isolada no leaf puro combo/quotaShareConcurrency.ts (unit-testado: estabilidade da key, no-op sem cap, serialização real, fail-open). Settings + schema + UI espelham comboCooldownWait. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * docs(resilience): document Quota-Share Concurrency Control (max_concurrent + serialization + cooldown-wait) (#4980) Documents the v3.8.36 quota-share concurrency layers in RESILIENCE_GUIDE.md: per-connection max_concurrent cap, the quota-share request serialization semaphore (FASE 2.1, qsconn:<connectionId>, fail-open, kill-switch), and the combo cooldown-aware retry — so operators know how to cap a subscription account's concurrency and why the routing gate alone cannot contain a single-connection flood. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(dashboard): proxy-pool success gating, sync timestamp, opt-in Redis (#4878) (#4988) Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(sse): fail over on 400 responses carrying rate-limit text (#4976) (#4986) * fix(sse): fail over on 400 responses carrying rate-limit text (#4976) * chore(quality): rebaseline accountFallback.ts file-size for #4976 fix --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(compression): stop RTK over-truncating file-read tool results (#4559) (#4987) * fix(compression): stop RTK over-truncating file-read tool results (#4559) * chore(quality): trim #4559 comment to keep rtk/index.ts within size cap --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(sse): honor per-account proxies and fingerprint rotation in opencode executor (#4954) (#4989) * fix(sse): honor per-account proxies and fingerprint rotation in opencode executor (#4954) * chore(quality): rebaseline auth.ts file-size for #4954 (+39: synthetic no-auth providerSpecificData hydration of fingerprints/accountProxies; irreducible credential-path wiring, covered by opencode-proxy-rotation-4954.test.ts + 159 auth/noauth regression) --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(sse): soft-penalize exhausted providers in auto-combo scoring (#4540) (#4990) * fix(sse): soft-penalize exhausted providers in auto-combo scoring (#4540) * chore(quality): document STATUS_SOFT_DEPRIORITIZE_FACTOR + rebaseline combo.ts for #4540 --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(dashboard): switch to visible filter after auto-hiding failed models in test-all (#4887) (#4991) * fix(dashboard): switch to visible filter after auto-hiding failed models in OAuth provider test-all (#4887) * test(dashboard): move #4887 test into tests/unit/ui so a CI runner collects it --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(pollinations): only enable jsonMode when JSON output is requested (#3981) (#5009) Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(antigravity): default safetySettings to all-OFF for parity with native Gemini paths (#5003) (#5008) * fix(antigravity): default safetySettings to all-OFF for parity with native Gemini paths (#5003) * docs(changelog): restore #3981 pollinations entry eaten by merge --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(chatgpt-web): map advertised gpt-5.5/5.4-pro/5.2-pro slugs to prevent silent model substitution (#4665) (#5010) * fix(chatgpt-web): map advertised gpt-5.5/5.4-pro/5.2-pro slugs to prevent silent model substitution (#4665) MODEL_MAP was missing the advertised catalog ids gpt-5.5, gpt-5.5-pro, gpt-5.4-pro and gpt-5.2-pro, so MODEL_MAP[model] ?? model sent the dot-form id verbatim to the ChatGPT backend-api, which silently rejected it and served the default Plus model. Map each to its dash-form slug. gpt-4-5 is already dash-form and falls through correctly, so it is intentionally left unmapped. Extends the executor MODEL_MAP test with the four ids and adds a drift guard asserting every advertised dot-form catalog id reaches the backend in dash-form (never verbatim), guarding future catalog<->map drift. file-size: tests/unit/chatgpt-web.test.ts frozen baseline 2809->2855 (+46) for the added test cases and drift-guard test; executor source unchanged in baseline. * docs(changelog): restore #3981/#5003 entries eaten by merge --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * feat(combos): add editable per-combo description field persisted via /api/combos (#5005) (#5011) * feat(combos): add editable per-combo description field persisted via /api/combos (#5005) * docs(changelog): restore #3981/#5003/#4665 entries eaten by merge --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * Fix Ollama Cloud max reasoning effort (#4993) Integrated into release/v3.8.36 * fix(copilot): replace execSync with execFile to prevent command injection (#5024) Integrated into release/v3.8.36 * fix(plugin): auth.json dual-key fallback for auto-prefix migration (#5027) Integrated into release/v3.8.36 * feat(endpoint): per-endpoint custom system prompt injection (#5022) Integrated into release/v3.8.36 * fix(headroom): translate openai-responses input through OpenAI for compression (#5023) Integrated into release/v3.8.36 * docs(changelog): add entries for #4993, #5024, #5027 (release notes credit) * fix(api): stop /api/system/env/repair 500 on packaged install (#5006) (#5028) * fix(api): stop /api/system/env/repair 500 on packaged install — lazy createRequire in sync-env.mjs (#5006) scripts/dev/sync-env.mjs ran createRequire(import.meta.url) at module top-level. When webpack bundles it into the standalone env-repair route, import.meta.url is frozen to the build-machine path (file:///home/runner/...) and createRequire throws during module evaluation, so the whole route module fails to load and every GET returns HTTP 500 — breaking the onboarding wizard on packaged/global installs. - Move createRequire into the guarded better-sqlite3 block (only place that needs it); a bad import.meta.url now returns the safe default. - resolveRootDir() falls back to process.cwd() when fileURLToPath throws. - route.ts passes an explicit rootDir (process.cwd()) so the helper never derives the root from the frozen import.meta.url, matching the .env target used by createEnvBackup(). - Regression guard: assert sync-env.mjs has no top-level createRequire + getEnvSyncPlan(oauth) works with explicit rootDir without throwing. * docs(changelog): restore #4993/#5023/#5024/#5027 + custom-system-prompt/headroom entries eaten by release merge * chore(quality): rebaseline 3 inherited base-reds from release merge Files NOT touched by this PR — grew on release/v3.8.36 via --admin merges and inherited here through 'git merge origin/release': - open-sse/executors/base.ts 1414->1416 (#4993 Ollama Cloud max-effort) - src/lib/db/settings.ts 1149->1151 (#5023 custom system prompt) - src/app/(dashboard)/.../endpoint/EndpointPageClient.tsx 2570->2612 (custom system prompt UI) * chore(release): finalize v3.8.36 CHANGELOG + docs (2026-06-25) --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com> Co-authored-by: Makcim Ivanov <makcimbx@gmail.com> Co-authored-by: Chewji <126886556+Chewji9875@users.noreply.github.com> Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com> Co-authored-by: Demiurge The Single <megamen932@gmail.com> Co-authored-by: Randi <55005611+rdself@users.noreply.github.com> Co-authored-by: Éder Costa <eder.almeida.costa@gmail.com> Co-authored-by: Jefferson Felizardo <jeffer1312@gmail.com> Co-authored-by: Arthur Bodera <abodera@gmail.com> Co-authored-by: Hamsa_M <116961508+hamsa0x7@users.noreply.github.com> Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com> |
||
|
|
db362b0126 |
Release v3.8.30 (#4267)
Release v3.8.30 — see CHANGELOG.md [3.8.30] for the full release notes. |
||
|
|
fa367dd99e |
Release v3.8.27 (#3968)
* chore(release): open v3.8.27 development cycle * fix(security): polynomial ReDoS in comboAgentMiddleware regex (#3982) * fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos) CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix. On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines (CodeQL js/polynomial-redos, alerts #612/#613). The surrounding runs are irrelevant to detecting/capturing the tag, so the detection pattern now matches only the core `<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the wrapping newlines (combo.ts streaming, #531) but BOUNDED ({0,16}) so it stays linear. Behavior preserved: detection, model extraction, multi-tag stripping (#454) and blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety regression tests (50k-newline inputs complete in <1ms). * docs(changelog): add #3982 ReDoS fix to [3.8.27] * ci(security): harden workflows — artipacked persist-credentials + cache-poisoning + SC2086 (#3965) * Refine provider quota card display (#3969) Integrated into release/v3.8.27 * feat: add sidebar group separator toggles (#3971) Integrated into release/v3.8.27 * Gate control-plane proxy direct fallback (#3963) Integrated into release/v3.8.27 * Capture actual upstream provider requests (#3941) Integrated into release/v3.8.27 * ci(quality): flip require-tighten + osv + Trivy to blocking (v3.8.27 cycle-end) (#3984) * fix(resilience): respect connection cooldown stored as numeric epoch (#3954) (#3995) rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved. * fix(providers): fetch live /models for LLM7 and BytePlus (#3976) (#3996) llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure. * fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref (#3972) (#3997) The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead. * feat(compression): add Indonesian caveman rules and language pack (#3975) Integrated into release/v3.8.27 (cherry picked from commit |
||
|
|
81a37b67ed |
Release v3.8.26 (#3875)
OmniRoute v3.8.26 — see CHANGELOG.md [3.8.26] for the full notes. Highlights: Vertex AI media generation (#3929), GLM-5.2 effort-tier routing (#3885), sticky round-robin combos (#3846), OpenRouter connection presets (#3878), compression prompt-cache fix (#3936/#3890), and a security pass (form-data/vite + workflow hardening, #3949). Co-authored-by: artickc <artickc@users.noreply.github.com> Co-authored-by: rdself <rdself@users.noreply.github.com> Co-authored-by: herjarsa <herjarsa@users.noreply.github.com> Co-authored-by: Jack Smith <16862258+YunyunZhai@users.noreply.github.com> Co-authored-by: dhaern <dhaern@users.noreply.github.com> Co-authored-by: adivekar-utexas <adivekar-utexas@users.noreply.github.com> Co-authored-by: megamen32 <megamen32@users.noreply.github.com> Co-authored-by: zhiru <zhiru@users.noreply.github.com> Co-authored-by: insoln <insoln@users.noreply.github.com> Co-authored-by: diego-anselmo <diego-anselmo@users.noreply.github.com> |
||
|
|
76a07cf7a5 |
Release v3.8.24 (#3747)
Release v3.8.24 — see CHANGELOG.md [3.8.24] for the full notes and the PR description for the contributors hall. Integration of release/v3.8.24 into main. |
||
|
|
7db430a352 |
Release v3.8.14 (#3340)
* chore(release): open v3.8.14 development cycle Version bump 3.8.13 -> 3.8.14 (root + electron + open-sse + openapi + lockfiles). Seed the v3.8.14 changelog with the four post-tag hotfixes that shipped to Docker/Electron in v3.8.13 but missed the immutable npm 3.8.13 (#3336 SSRF / CodeQL #323, #3334/#3335/#3339 Electron packaging). i18n CHANGELOG mirrors get the in-progress placeholder section. * feat: add per-provider custom headers support for OpenAI/Anthropic-compatible nodes (#3338) Integrated into release/v3.8.14 * fix: Kiro Builder ID token import fails with Bad credentials (#3333) Integrated into release/v3.8.14 — adds Builder ID cached-creds + OIDC refresh path for Kiro token import, with regression tests (#3333). * Improve code quality: auto-pr/docstrings-1780792063 (#3337) Integrated into release/v3.8.14 — docstring for context analytics route re-export. * fix(catalog): remove minimaxai/minimax-m3 from NVIDIA NIM tier (404 upstream) (#3329) (#3341) NVIDIA NIM does not host minimaxai/minimax-m3 — every request returns 404 page not found, while sibling minimaxai/minimax-m2.7 on the same provider works. Advertising a model that 404s is a catalog bug; remove it from the nvidia tier (it remains on the tiers that actually serve MiniMax M3). Re-add only once NVIDIA serves it. Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com> * fix(cli): write OpenCode config to ~/.config on all platforms incl. Windows (#3330) (#3343) resolveOpencodeConfigDir used %APPDATA% on Windows, but OpenCode reads its config from XDG ~/.config/opencode/ on every platform (on Windows: %USERPROFILE%\.config\opencode\, NOT %APPDATA%). So a Windows user who configured OpenCode via the dashboard had the file written where OpenCode never looks — it silently had no effect. Use the XDG path (XDG_CONFIG_HOME || ~/.config) unconditionally. Update the UI note + route JSDoc, and flip the three tests that encoded the old %APPDATA% behavior (t40 per-platform + card-note, cli-runtime-extended getCliConfigPaths). Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com> * fix(proxy): make auto-selection fallback opt-in (#3332) (#3344) selectWorkingProxyFallback (Step 11 of resolveProxyForConnection) listed ALL registry proxies, ignoring assignments and per-connection proxy_enabled, and returned the first working one with level:'autoSelect'. So a single proxy added to the registry silently became a global fallback for every connection's traffic. Gate it behind a new PROXY_AUTO_SELECT_ENABLED feature flag (default off): the fallback now no-ops unless the operator opts in. No registry proxy becomes a silent global default anymore. Co-authored-by: hertznsk <hertznsk@users.noreply.github.com> * fix(sse): treat MiniMax M3 as multimodal so vision isn't stripped (#3328) (#3342) MiniMax M3 via the opencode provider (oc/minimax-m3-free) appeared blind: image inputs didn't reach the model, while the same model in Cline could see them. Verified empirically that MiniMax M3 on the opencode upstream IS multimodal -- a base64 image is described correctly (it returns 403 only for remote image URLs, which it doesn't accept). Root cause: OmniRoute treated MiniMax M3 as a non-vision model in two places, so when compression was active the image was replaced with a text placeholder before dispatch: - compression's modelSupportsVision() heuristic (lite.ts) only matched gpt-4/4o/claude-3/gemini/vision -- minimax was absent -> replaceImageUrls stripped the image. - the opencode minimax-m3-free catalog entry lacked supportsVision, so the combo vision-capability gate could also exclude/mishandle it. Add 'minimax-m3' to the vision heuristic and supportsVision: true to the opencode minimax-m3-free entry. TDD: a failing-then-passing test in compression/lite.test.ts proves replaceImageUrls now keeps images for minimax-m3 ids, plus a registry assertion mirroring the #2822 qwen test. Reported-by: @mikmaneggahommie * docs(i18n): translate 25 core documentation files to Indonesian (#3348) Integrated into release/v3.8.14 — Indonesian i18n docs. * fix(review): resolve /review-reviews battery findings (LEDGER-1..11) on v3.8.14 (#3350) Integrated into release/v3.8.14 — /review-reviews battery hardening (LEDGER-1..11) for #3338 custom-headers + #3333 kiro, plus cycle-test drift fixes (#3329/#3330/#3332). * fix(provider-proxy): honor per-account proxy toggles (#3349) Integrated into release/v3.8.14 — honor per-account proxy toggles + auto-fallback opt-in via PROXY_AUTO_SELECT_ENABLED. * fix(dashboard): remove duplicate Distribute Proxies button on provider page (#3352) * fix(providers): reduce proxy label noise (#3346) Integrated into release/v3.8.14 — reduce proxy label noise + a11y (aria-label/sr-only). * fix(duckduckgo): restore bare Response contract and rebase onto release/v3.8.14 (#3323) Integrated into release/v3.8.14 — browser-backed cookie providers (duckduckgo/claude-web) with restored executor contract + unit tests. * fix(noauth): expose only usable model aliases (#3345) Integrated into release/v3.8.14 — noauth usable-alias filtering + registry alias plumbing (veo-free). * fix(dashboard): stop infinite config-load loop on Hermes Agent detail page (#3353) * fix(electron): tree-kill the server on exit/update to release the omniroute.exe lock (#3347) (#3354) * chore(release): finalize v3.8.14 changelog + clear release-gate drift - CHANGELOG: finalize the v3.8.14 section (date, full New Features/Bug Fixes/ Maintenance coverage of all 16 cycle commits, Contributors hall of 12). - docs: document OMNIROUTE_BROWSER_POOL + WEB_COOKIE_USE_BROWSER (#3323) in .env.example + ENVIRONMENT.md; regenerate the id/llm.txt strict mirror (#3348 had translated it; llm.txt mirrors must match root). - test(proxy-fetch): #3323 made tlsClient.available a computed getter — stub it via Object.defineProperty instead of assignment (5 tests were red on the base). * fix(translator): coerce Gemini functionDeclaration parameters to an OBJECT schema (#3357) (#3360) * fix(gemini): resolve truncation/suppression of false positive textual tool call markers in backticks (#3358) Integrated into release/v3.8.14 — Gemini/Antigravity textual tool-call marker normalization (no false-positive suppression + split-chunk buffering). * docs(changelog): add #3358 Gemini textual tool-call normalization to v3.8.14 * fix(dashboard): surface real analytics error instead of generic placeholder (#3356) (#3361) The Analytics page discarded the server's error body on a non-OK response and rendered a generic "An error occurred", so users (and maintainers) could not see why /api/usage/analytics 500'd after an upgrade. Now the route returns the real reason via buildErrorBody (sanitized, Hard Rule #12) and the page surfaces it via a new readFetchErrorMessage helper that handles both the OpenAI-style and legacy error shapes. Reported-by: @superti4r --------- Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com> Co-authored-by: Someres <168349709+quanturbo@users.noreply.github.com> Co-authored-by: Dong Mengzhe <154944819+Lang-Qiu@users.noreply.github.com> Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com> Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com> Co-authored-by: hertznsk <hertznsk@users.noreply.github.com> Co-authored-by: Krisna Santosa <54174372+KrisnaSantosa15@users.noreply.github.com> Co-authored-by: Randi <55005611+rdself@users.noreply.github.com> Co-authored-by: Wilson <pedbookmed@gmail.com> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: Ardem2025 <ardemb22@gmail.com> |
||
|
|
4dbbbaacf1 |
fix: NVIDIA NIM API key validation timeout (bypass proxy fetch patch) (#3226)
NVIDIA NIM validation bypasses the proxy-patched fetch (504 fix) + combined with #3116 reliable probe model + test. Integrated into release/v3.8.11. |
||
|
|
de5c842301 |
feat(proxy): auto-fallback proxy selection when validation fails (#3171)
Integrated into release/v3.8.11 |
||
|
|
191009dd23 |
Release v3.8.7 (#2919)
* feat(plugins): WordPress-style plugin system backend * fix(plugins): address code review feedback - Path traversal guard: validate entryPoint stays within plugin dir - install() now handles direct plugin directories (not just parent dirs) - Non-null assertion replaced with explicit null check - require efficiency: allowedModules map moved outside function - Source wrapper: add newlines to prevent trailing comment issues - Config validation: validate values against configSchema on save - Dynamic import comment: clarify Node.js caching behavior Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> * fix(plugins): replace vm with child_process, add auth to all routes Addresses all remaining code review feedback: 1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork() for proper process-level isolation. Complies with Rule 3 (no eval). Each plugin runs in a separate Node.js process with IPC communication. 2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin API route files (list, install, scan, details, activate, deactivate, config). 3. **Env filtering**: Only safe env vars passed to plugin processes unless "env" permission is granted. Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> * fix(plugins): security + ESM fixes for loader and manager loader.ts: - Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort - Fix ESM: write host script as .mjs (not .js) to force ESM execution - Add timeout: 10s default on callHook() with Promise.race - Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace - Fix env filtering: use allowlist (safeKeys) instead of passing all env vars - Clear timeout on successful IPC response (no timer leak) manager.ts: - Fix path traversal: use fs.realpath() instead of startsWith() - Fix imports: use registerHook/unregisterHooks from hooks.ts - Register hooks individually via registerHook(event, name, handler) hooks.ts: - Copied from feat/plugin-custom-hooks (canonical registry) * feat(discovery): add discovery tool stub service Phase 1 scaffold for automated provider discovery: - DiscoveryConfig, DiscoveryResult types - probeEndpoint() for URL availability checking - scanProvider() stub (Phase 2 will implement real scanning) - getDiscoveryResults() stub - Default config: disabled (opt-in) * chore(plugins): slop cleanup — pino logger, remove redundant sorts - index.ts: replace console.log/error with pino structured logging - hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration) - manager.ts: add readFile import * test(plugins): add scanner, loader, manager unit tests - scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple) - loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces) - manager: 6 tests (singleton, lifecycle methods, error on unknown) - Total: 20 tests, all passing * fix(settings): add missing home page pin keys to updateSettingsSchema * feat(plugins): add i18n keys to all 42 locales * fix(settings): add missing security keys to updateSettingsSchema and add tests * fix(usage): analytics route reads combo_name/requested_model from call_logs only The 3.8.6 variant of #2904 added SELECTs of combo_name/requested_model against usage_history, but those columns only exist in call_logs (no migration adds them to usage_history). This returned HTTP 500 on /api/usage/analytics. Restore the working query shape from the 3.8.7 variant. Fixes 18 failing usage-analytics-route tests. * fix(types,test): resolve noImplicitAny in progressiveAging + align semaphore test to #2903 gate pruning - progressiveAging: type compression results so messages[0].content is indexable (was TS7053 against {}); restores typecheck:noimplicit:core gate. - services-branch-hardening: #2903 (perf-ram) prunes idle rate-limit gates on zero; assert no-running/empty-queue without assuming the entry persists. * fix(analytics): address merged review regressions * fix(executor): normalize max effort for openai shape providers * Make zero-latency combo optimizations opt-in * Address zero-latency combo review feedback * chore(release): sync v3.8.7 touchpoints + credit contributors - llm.txt → 3.8.7 (Current version + Key Features header) - CHANGELOG: add Dmitry Kuznetsov & Nikolay Alafuzov to 3.8.6 Hall of Contributors - version already 3.8.7 across package.json/open-sse/electron/openapi (from #2909) * fix(cleanup): restore usage history cutoff boundary * docs(changelog): rank 3.8.6 contributors in a commits table with their PRs * fix(dashboard): theme ReactFlow Controls +/- buttons for dark mode * fix(settings): add missing home page pin keys to updateSettingsSchema * fix(settings): add missing security keys to updateSettingsSchema and add tests * fix(executor): normalize max effort for openai shape providers * Make zero-latency combo optimizations opt-in * Address zero-latency combo review feedback * fix(analytics): address merged review regressions * fix(cleanup): restore usage history cutoff boundary * feat(plugins): WordPress-style plugin system backend * fix(plugins): address code review feedback - Path traversal guard: validate entryPoint stays within plugin dir - install() now handles direct plugin directories (not just parent dirs) - Non-null assertion replaced with explicit null check - require efficiency: allowedModules map moved outside function - Source wrapper: add newlines to prevent trailing comment issues - Config validation: validate values against configSchema on save - Dynamic import comment: clarify Node.js caching behavior Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> * fix(plugins): replace vm with child_process, add auth to all routes Addresses all remaining code review feedback: 1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork() for proper process-level isolation. Complies with Rule 3 (no eval). Each plugin runs in a separate Node.js process with IPC communication. 2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin API route files (list, install, scan, details, activate, deactivate, config). 3. **Env filtering**: Only safe env vars passed to plugin processes unless "env" permission is granted. Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> * fix(plugins): security + ESM fixes for loader and manager loader.ts: - Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort - Fix ESM: write host script as .mjs (not .js) to force ESM execution - Add timeout: 10s default on callHook() with Promise.race - Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace - Fix env filtering: use allowlist (safeKeys) instead of passing all env vars - Clear timeout on successful IPC response (no timer leak) manager.ts: - Fix path traversal: use fs.realpath() instead of startsWith() - Fix imports: use registerHook/unregisterHooks from hooks.ts - Register hooks individually via registerHook(event, name, handler) hooks.ts: - Copied from feat/plugin-custom-hooks (canonical registry) * feat(discovery): add discovery tool stub service Phase 1 scaffold for automated provider discovery: - DiscoveryConfig, DiscoveryResult types - probeEndpoint() for URL availability checking - scanProvider() stub (Phase 2 will implement real scanning) - getDiscoveryResults() stub - Default config: disabled (opt-in) * chore(plugins): slop cleanup — pino logger, remove redundant sorts - index.ts: replace console.log/error with pino structured logging - hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration) - manager.ts: add readFile import * test(plugins): add scanner, loader, manager unit tests - scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple) - loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces) - manager: 6 tests (singleton, lifecycle methods, error on unknown) - Total: 20 tests, all passing * feat(plugins): add i18n keys to all 42 locales * chore(plugins): remove duplicate migration 059_create_plugins.sql * chore(plugins): remove duplicate migration 059_create_plugins.sql (post-merge) * fix(sse): guard non-string error.code in proxyFetch + harden model parsing (#2463) (#2923) Integrated into release/v3.8.7 * fix(docker): add runner-web stage with Playwright Chromium (#2832) (#2846) Integrated into release/v3.8.7 * docs(changelog): document NVIDIA NIM and error code type-crash fix (#2463) * test: ignore NVIDIA_BASE_URL and NVIDIA_MODEL in env contract check --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com> Co-authored-by: Halil Tezcan KARABULUT <info@hlltzcnkb.com> Co-authored-by: R.D. <rogerproself@gmail.com> |
||
|
|
f59f8daa94 |
Release v3.8.6 (#2804)
* fix(gemini): preserve structured tool calls for antigravity * fix(gemini): parse prefixed textual tool calls * fix(antigravity): preserve textual SSE tool calls * fix(stream): normalize textual passthrough tool calls * fix(stream): normalize split textual tool calls * fix(stream): suppress malformed textual tool calls * fix(stream): suppress compact malformed tool calls * fix(stream): emit structured textual tool calls * fix(stream): suppress unknown textual tool calls * fix(stream): normalize responses textual tool calls * chore: ignore .claude/settings.local.json (per-user Claude Code permissions) * fix(opencode-go): route qwen3.x via claude messages + repair fixMissingToolResponses for Claude-shape upstreams (#2791) Integrated into release/v3.8.6 * fix: resolve npm install warnings — remove dead deps, relax engine constraint (#2792) Integrated into release/v3.8.6 * fix: register missing web-cookie validators (claude-web, gemini-web, copilot-web, t3-web) (#2793) Integrated into release/v3.8.6 * fix: Error: Unable to inspect existing database #2771 (#2795) Integrated into release/v3.8.6 * fix(oauth): repair Google loopback callback flow (#2796) Integrated into release/v3.8.6 * feat(logs): add clean history button (#2799) Integrated into release/v3.8.6 * [codex] home: restore settings-driven home layout and quota auto-refresh (#2800) Integrated into release/v3.8.6 * fix(gemini): emit signaturelessToolCallMode:text for GEMINI format models (#2801) Integrated into release/v3.8.6 * feat(modelSpecs): align opencode-go family with upstream provider limits (#2802) Integrated into release/v3.8.6 * chore: apply unit test fixes, polyfills, and environment precedence fixes * docs(agents): atualiza fluxos de release e triagem Expande os workflows de release para incluir auditoria de segurança, CHANGELOG completo por commits, quality gate obrigatório, homologação em VPS local, publicação oficial, deploy em Akamai e validação de artefatos. Reorganiza a triagem de features com arquivos permanentes por bucket, suporte a itens em andamento, regra de reclaim após 15 dias e novo tratamento para ideias viáveis catalogadas. Corrige a orientação de revisão de discussões para usar a ordem cronológica real dos comentários e respostas ao identificar a última atividade. * fix(lockout): classify Gemini Antigravity resource exhaustion as quota_exhausted * fix(reasoning): gate replay by interleaved field * docs(rule-16): permit human Co-authored-by, restrict only AI/bot trailers Rule #16 previously banned all `Co-Authored-By` trailers absolutely. That blocked the upstream-port workflows (`/port-upstream-features` and `/port-upstream-issues`), which must credit human upstream PR authors and issue reporters in OmniRoute commits. Refine the rule to ban only AI/bot-attributed trailers (Claude, GPT, Copilot, Bot; anthropic.com / openai.com / bot-owned noreply.github.com emails) while allowing standard human `Co-authored-by: Name <email>` attribution. Sync the rule across the source CLAUDE.md, the E2E shakedown doc note, and 41 i18n translations. * fix(gitlawb): add specialty validators for connection test — bypass /models probe GitLawB OpenGateway API (xiaomi-mimo compatible) does not expose a /models endpoint, causing validateOpenAILikeProvider to 404 on the initial probe and report 'Provider validation endpoint not supported'. Add specialty validators for both gitlawb and gitlawb-gmi that follow the same pattern as the existing xiaomi-mimo validator: skip GET /models, validate directly via POST /chat/completions with a minimal test message. Any 401/403 response means an invalid key; all other responses mean auth is OK. Fixes test-connection returning 404 for GitLawB providers. * test(gitlawb): add 12 unit tests for gitlawb and gitlawb-gmi specialty validators Covers success, auth failure (401/403), non-auth acceptance (400/422/429), network errors, and custom baseUrl overrides for both providers. * feat(gitlawb): serve models from static registry without API-unavailable warning GitLawB's OpenGateway API does not expose a /models endpoint per provider-path. Previously the models route fell through to the generic fallback which returned static catalog models with the misleading 'API unavailable — using local catalog' warning. Now gitlawb and gitlawb-gmi are handled as static model providers (same pattern as reka and qwen OAuth) — models are served from the provider registry without any warning, since all registered models are functional via POST /chat/completions. * refactor(gitlawb): extract shared opengateway validator factory, fix docs path in test - Extract gitlawb/gitlawb-gmi validators into buildOpengatewayValidator factory - Fix dockerignore-docs-coverage test: update stale docs/AUTO-COMBO.md -> docs/routing/AUTO-COMBO.md * fix(reasoning): guard interleaved capability lookup * feat(gitlawb): dynamic model fetch with gmi-cloud fallback Hybrid approach: - gitlawb (xiaomi-mimo): dynamic /models endpoint → 356 models - gitlawb-gmi (gmi-cloud): 404 fallback → local catalog gracefully Mimics Gitlawb/openclaude's model-routing pattern * i18n(pt-BR): complete missing translations and sync with en.json * feat(build): nix multi-OS package manager install (#2806) Integrated into release/v3.8.6 * fix(i18n): translate 144 new __MISSING__ pt-BR strings (#2816) Integrated into release/v3.8.6 * chore(docs): set coverage gate to 40/40/40/40 in CLAUDE.md Aligns the documented coverage gate with the v3.8.6 release decision (lowered from 75/75/75/70). Matches the threshold already set in package.json by the large feature PRs (planos 11-22). * fix(cli): respect PORT env var in serve command (#2845) Integrated into release/v3.8.6. * fix(deepseek-web): return 400 when client sends tools[] - chat.deepseek.com has no tool support (#2854) Integrated into release/v3.8.6. * fix(qoder): reject invalid/expired PATs returning Cosy 500 error (#2860) Integrated into release/v3.8.6. * fix(cli): register openclaw in tool-detector (#2833) (#2850) Integrated into release/v3.8.6. * fix(api): include noAuth providers in /v1/models catalog (#2798) (#2814) Integrated into release/v3.8.6. * fix(combo): resolve custom provider targets via combo name (#2778) (#2812) Integrated into release/v3.8.6. * fix(translator): strip safety_identifier in openai-responses cleanup (#2770) (#2809) Integrated into release/v3.8.6. * fix(quota): honor explicit per-connection preflight opt-out (#2831) (#2844) Integrated into release/v3.8.6. * fix(usage): un-invert GitHub Copilot Free/limited quota — limited_user_quotas is remaining (#2876) (#2881) Integrated into release/v3.8.6. * fix(nous-research): correct baseUrl to include /chat/completions (#2826) (#2835) Integrated into release/v3.8.6. * fix(opencode): qwen3.x max/plus models lack vision support (#2822) (#2836) Integrated into release/v3.8.6. * fix(translator): pass-through tool_search built-in tool type (#2766) (#2811) Integrated into release/v3.8.6. * fix(github): route claude-opus-4.6 via chat completions (#2821) Integrated into release/v3.8.6. * docs(oauth): add Windsurf login fix design (Phase 1 hotfix + Phase 2 Firebase OAuth) Two-phase plan to fix the broken Windsurf OAuth flow: - Phase 1: drop the dead app.devin.ai/editor/signin PKCE path, promote import-token from windsurf.com/show-auth-token as the primary path - Phase 2: port Firebase OAuth + RegisterUser flow from fendoushaonian/WindSurf-gRPC-API for full browser-based automation Spec only - no code changes yet. * docs(plan): Phase 1 windsurf login hotfix implementation plan 10 tasks covering: - TDD assertions for flowType + 410 Gone responses - Provider switch to import_token - Route handler retiring authorize/start-callback-server/poll-callback - OAuthModal UI override - i18n sync - Verification + PR steps * fix(cli): replace cli-table3 with hand-rolled formatter (#2752) (#2813) Integrated into release/v3.8.6. * fix(skills): skip interception for unregistered client-native tools (#2815) (#2817) Integrated into release/v3.8.6. * feat(sse): add RTK filters for kubectl, docker-build, composer, gh (#2824) Integrated into release/v3.8.6. * fix(geminiHelper): support rec.image content shape + warn on dropped remote URLs (refs #2807) (#2855) Integrated into release/v3.8.6. * fix(cli): allow nullable/optional apiKey in cliMitmStartSchema (#2857) Integrated into release/v3.8.6. * fix(combo): preserve system messages during context handoff summary generation (#2865) Integrated into release/v3.8.6. * fix: wire CLIProxyAPI fallback settings into chatCore routing engine (#2866) Integrated into release/v3.8.6. * fix(usage): add opencode quota fetcher (#2852) (#2867) Integrated into release/v3.8.6. * feat(claude): default xhigh support for newer Opus models (#2874) Integrated into release/v3.8.6. * fix(cli): restore omniroute logs command stream (#2756) (#2810) Integrated into release/v3.8.6. * fix(combo): normalize upstream Headers for Node 24 undici interop (#2751) (#2823) Integrated into release/v3.8.6. * Rename proxy log Public IP to Client IP (#2880) Integrated into release/v3.8.6. * fix(claude): preserve max effort for supported models (#2875) Integrated into release/v3.8.6. * fix(oauth): switch windsurf provider to import_token flow The PKCE auth URL targeting app.devin.ai/editor/signin returns 404 post-rebrand. Until Phase 2 ports Firebase OAuth + RegisterUser, the only supported path is import-token via windsurf.com/show-auth-token. - windsurf.ts: drop buildAuthUrl, set flowType=import_token - generateAuthData returns supported:false + helpful error for windsurf/devin-cli - tests: assert flowType + disabled stub * fix(oauth): return 410 Gone for retired windsurf/devin-cli PKCE actions start-callback-server, authorize, and poll-callback (GET + POST) now return 410 Gone with a pointer to /import-token. The 410 short-circuit runs before auth so the response is honest about the action being permanently gone, not gated. Codex PKCE flow unchanged. Tests: 5 new assertions cover GET + POST 410 paths and a Codex regression check. * refactor(oauth): annotate retired PKCE fields in WINDSURF_CONFIG No behaviour change - comment-only update documenting that authorizeUrl, codeChallengeMethod, callbackPort, callbackPath, apiServerUrl, and exchangePath are no longer consumed. Active fields (inferenceUrl, showAuthTokenUrl, firebaseApiKey, ideName) called out separately. * fix(cli,docs): use requireCliToolsAuth in logs route + document OPENCODE quota env Post-merge contract fixes for v3.8.6: - src/app/api/cli-tools/logs/route.ts (#2810) now uses the shared requireCliToolsAuth guard (param renamed req->request) to satisfy the cli-tools-auth-hardening contract test. - Document OMNIROUTE_OPENCODE_QUOTA_URL (#2867) in docs/reference/ENVIRONMENT.md to satisfy the env/docs sync contract. * fix(dashboard): force import-token panel for windsurf/devin-cli Phase 1 hotfix: hide the 'Browser Login' tab and start in Paste API Key mode. Removes windsurf/devin-cli from PKCE_CALLBACK_SERVER_PROVIDERS so no callback server is started for them. Codex still uses the PKCE flow. The 'Get token' link continues to point at windsurf.com/show-auth-token via the existing supportsTokenPaste form copy. * fix(oauth): windsurf import-token mapTokens signature mismatch The route at `src/app/api/oauth/[provider]/[action]/route.ts` invokes `providerData.mapTokens({ accessToken: token })` (object), matching the cursor/kiro signature. The windsurf provider was declared with `mapTokens(token: string)` instead, so the entire object was stored as `accessToken`. When the connection record reached the SQLite layer it crashed with: SQLite3 can only bind numbers, strings, bigints, buffers, and null Fix by aligning windsurf's `mapTokens` signature with the route caller and the cursor/kiro convention. Also dedupe a copy-pasted second `if (action === "import-token")` block in the route handler — the second block was unreachable but identical to the first. Adds two regression tests asserting that `provider.mapTokens({ accessToken })` returns a string `accessToken` for both windsurf and devin-cli, so a future signature drift trips the gate instead of the SQLite bind error in production. * feat(compression): expand pt-BR pack with troglodita rules (15 → 49) (#2818) Integrated into release/v3.8.6 * fix(sse): repair RTK engine defaults so dedup and direct calls work (#2825) Integrated into release/v3.8.6 * fix(mcp): redirect console.log/warn to stderr in --mcp stdio mode (#2840) Integrated into release/v3.8.6 * fix(gemini-cli): prefer real project IDs over default-project (#2841) Integrated into release/v3.8.6 * fix(opencode-go): add provider limits quota fetcher (#2861) Integrated into release/v3.8.6 * Audit & add web cookie providers: fix 4 missing registry entries + DuckDuckGo (#2862) Integrated into release/v3.8.6 * fix(antigravity): harden signatureless tool history (#2878) Integrated into release/v3.8.6 * fix: provider model sync pruning and dynamic antigravity MITM proxy mappings (#2886) Integrated into release/v3.8.6 * feat(usage): per-API-key token limits scoped to model/provider/global (#2888) Integrated into release/v3.8.6 * fix(audio): build multipart body manually to preserve Content-Type (#2842) Integrated into release/v3.8.6 * refactor: remove agent skill documentation files and streamline maintenance workflows * test(stabilization): resolve unit test failures in blackbox-web, schema-coercion, translator-helper-branches, usage-service-hardening, and audio-transcription * fix(security): mitigate Socket.dev supply-chain findings + secrets opt-in + minimal build profile (#2863) (#2871) Two real security gaps closed and four cosmetic Socket.dev fingerprints removed. See docs/security/SOCKET_DEV_FINDINGS.md for the per-finding maintainer attestation. Real bugs fixed: - cloudSync: HMAC verification of `X-Cloud-Sig` + opt-in `OMNIROUTE_CLOUD_SYNC_SECRETS=true` before overwriting `accessToken` / `refreshToken` / `providerSpecificData` from a remote response. Closes the silent-credential-swap surface (a misconfigured or hostile CLOUD_URL could previously replace local tokens unverified). - Zed import: split into 2-step `/discover` + `/import` flow. `/import` now requires `confirmedAccounts: [{ service, account, fingerprint }]` and re-reads the keychain server-side to filter by fingerprint, so a tampered discover response cannot trick the endpoint into saving an unrelated token. Cosmetic Socket.dev mitigations: - runElevatedPowerShell writes the elevated payload to a per-call temp `.ps1` file (mode 0o600) and references it via `-File`. Removes the textbook `-EncodedCommand <base64utf16le>` pattern flagged as malware by Socket's AI classifier. - Maintainer attestation `SECURITY-AUDITOR-NOTE:` blocks added at every flagged call site pointing to `docs/security/SOCKET_DEV_FINDINGS.md`. Build-time hardening: - `OMNIROUTE_BUILD_PROFILE=minimal` (`npm run build:secure`) physically removes the four sensitive modules from the standalone bundle via webpack `NormalModuleReplacementPlugin`. Stubs throw `FeatureDisabledError` at runtime. Intended for the `omniroute-secure` artifact. Tests: - 24 new unit tests in `tests/unit/security/` covering the wrapper builder, HMAC verification (4 cases), credential fingerprint determinism (5 cases), confirmedAccounts validation + fingerprint filtering (6 cases), and the minimal-build stubs (5 cases). Docs: - New `docs/security/SOCKET_DEV_FINDINGS.md` — per-finding attestation. - New `socket.yml` — Socket.dev v2 config pointing at the attestation. - Updated `SECURITY.md` — supply-chain scanner section. - Updated `.env.example` — three new env vars documented. Backwards compatibility: - Cloud sync token overwrite is OFF by default. Users who relied on it must set `OMNIROUTE_CLOUD_SYNC_SECRETS=true`. Breaking change documented in CHANGELOG. - Zed import 2-step is the new default; legacy 1-step preserved behind `OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP=true` and will be removed in v3.9. Closes #2863 * fix(security): redact public Firebase Web key from windsurf spec; doc SHA-256 cache-key rationale (#2894) Two security-scanning findings on release/v3.8.6: - Secret-scanning alert 7 (google_api_key): the windsurf login-fix design spec embedded the literal public Firebase Web API key on two lines. Firebase Web API keys are non-sensitive by design (they identify the project; access is gated by Firebase Security Rules + key restrictions), but the literal trips secret scanning. Redacted to a placeholder; the embedded default still goes through resolvePublicCred per rule #11. - Code-scanning alert 261 (js/insufficient-password-hash): tokenCacheKey() uses SHA-256 to derive an in-memory cache key from the session token, not for password-at-rest storage. Added a comment documenting why CWE-916 KDFs do not apply (false positive). * fix(ci): resolve release/v3.8.6 gate failures (docs-sync, any-budget, pack-artifact) (#2895) * fix(ci): resolve release/v3.8.6 gate failures (docs-sync, any-budget, pack-artifact) Three CI gates failed on release/v3.8.6 (run 26630300877): - docs-sync: CHANGELOG had a spurious "## [3.8.6-patch]" section above "## [3.8.6]", so the latest release no longer matched package.json (3.8.6) and the 41 i18n CHANGELOG mirrors were flagged as missing that section. Fold the lone #2752 entry into [3.8.6] and drop the patch heading. - any-budget:t11: open-sse/handlers/chatCore.ts regressed to 1 explicit `any` (budget 0). Type the persist callback arg as Record<string, unknown>, which matches runWithOnPersist's RefreshPersistFn contract exactly. - pack-artifact: open-sse/utils/setupPolyfill.ts ships via package.json "files" (bin/omniroute.mjs imports it at startup) but was missing from the pack policy allowlist. Allow it and add a regression test. * fix(security): redact public Firebase Web key from windsurf spec Redact the literal public Firebase Web API key (secret-scanning #7) to a placeholder, mirroring the redaction on release/v3.8.6 (PR #2894) and the windsurf fix branch. Non-sensitive public Web key; trips secret scanning. * feat(combo): Zero-Latency Combos (Hedging, Proactive Compression, Predictive TTFT) (#2868) * feat(combo): implement zero-latency combo optimizations (hedging, proactive compression, predictive TTFT) * fix(combo): fix predictive TTFT skip logic and unhandled promise rejections --------- Co-authored-by: Automation <automation@omniroute> * feat: implement automated skill workflows and update system configuration and validation schemas * test: eliminate dynamic cast warnings in cloud-sync unit test * test: isolate services-branch-hardening database directory to avoid concurrency issues * feat(providers): add 7 new web-cookie providers + research catalog + discovery tool New providers: - huggingchat: free LLM chat via huggingface.co/chat (no subscription) - phind: free dev-focused AI chat via phind.com/api/agent - poe-web: multi-model chat via poe.com GraphQL (p-b cookie) - venice-web: privacy-focused AI chat via venice.ai (session cookie) - v0-vercel-web: Vercel v0 code gen via v0.dev (session cookie) - kimi-web: Moonshot Kimi chat via kimi.moonshot.cn (session cookie) - doubao-web: ByteDance Doubao chat via doubao.com (session cookie) Additional: - Research catalog: docs/research/UNLIMITED_LLM_ACCESS.md - Discovery tool design + stub: src/lib/discovery/ + migration 073 - Unit tests: 33 tests for all 7 providers - Shared helpers consolidated in error.ts (slop cleanup) - All registered in WEB_COOKIE_PROVIDERS + providerRegistry + webSessionCredentials Closes #2885 * fix(typecheck): resolve typecheck errors in combo spec and compression modules * feat(api,oauth): add `agy` (Antigravity CLI) standalone provider with CLI token import (#2899) Add a standalone OAuth provider `agy` (Antigravity CLI) next to gemini-cli/antigravity. It reuses the antigravity inference backend (identical Google client_id + daily-cloudcode-pa.googleapis.com endpoint, executor and token-refresh) but ships its own model catalog — including the Claude models the backend exposes (claude-opus-4-6-thinking, claude-sonnet-4-6) — its own account pool, and four ways to connect: - token-file import (paste/upload the agy oauth token JSON) - auto-detect a local CLI login (~/.gemini/antigravity-cli/antigravity-oauth-token) - browser OAuth (via the shared OAuthModal Google loopback flow) - bulk / ZIP import New routes: POST /api/providers/agy-auth/{import,import-bulk,zip-extract,apply-local}. Catalog pinned from the live :fetchAvailableModels endpoint. Docs (openapi.yaml, ENVIRONMENT.md, .env.example, CHANGELOG) updated; new unit tests for registration, the token parser, and route auth-hardening. * fix(security): redact public Firebase Web key from windsurf spec (#2896) Redact the literal public Firebase Web API key (secret-scanning #7) to a placeholder. Firebase Web API keys are non-sensitive by design but the literal trips GitHub secret scanning. Mirrors the redaction landed on release/v3.8.6 (PR #2894). Embedded default still flows through resolvePublicCred (rule #11). * Pr 2871 (#2897) * fix(security): mitigate Socket.dev supply-chain findings + secrets opt-in + minimal build profile (#2863) Two real security gaps closed and four cosmetic Socket.dev fingerprints removed. See docs/security/SOCKET_DEV_FINDINGS.md for the per-finding maintainer attestation. Real bugs fixed: - cloudSync: HMAC verification of `X-Cloud-Sig` + opt-in `OMNIROUTE_CLOUD_SYNC_SECRETS=true` before overwriting `accessToken` / `refreshToken` / `providerSpecificData` from a remote response. Closes the silent-credential-swap surface (a misconfigured or hostile CLOUD_URL could previously replace local tokens unverified). - Zed import: split into 2-step `/discover` + `/import` flow. `/import` now requires `confirmedAccounts: [{ service, account, fingerprint }]` and re-reads the keychain server-side to filter by fingerprint, so a tampered discover response cannot trick the endpoint into saving an unrelated token. Cosmetic Socket.dev mitigations: - runElevatedPowerShell writes the elevated payload to a per-call temp `.ps1` file (mode 0o600) and references it via `-File`. Removes the textbook `-EncodedCommand <base64utf16le>` pattern flagged as malware by Socket's AI classifier. - Maintainer attestation `SECURITY-AUDITOR-NOTE:` blocks added at every flagged call site pointing to `docs/security/SOCKET_DEV_FINDINGS.md`. Build-time hardening: - `OMNIROUTE_BUILD_PROFILE=minimal` (`npm run build:secure`) physically removes the four sensitive modules from the standalone bundle via webpack `NormalModuleReplacementPlugin`. Stubs throw `FeatureDisabledError` at runtime. Intended for the `omniroute-secure` artifact. Tests: - 24 new unit tests in `tests/unit/security/` covering the wrapper builder, HMAC verification (4 cases), credential fingerprint determinism (5 cases), confirmedAccounts validation + fingerprint filtering (6 cases), and the minimal-build stubs (5 cases). Docs: - New `docs/security/SOCKET_DEV_FINDINGS.md` — per-finding attestation. - New `socket.yml` — Socket.dev v2 config pointing at the attestation. - Updated `SECURITY.md` — supply-chain scanner section. - Updated `.env.example` — three new env vars documented. Backwards compatibility: - Cloud sync token overwrite is OFF by default. Users who relied on it must set `OMNIROUTE_CLOUD_SYNC_SECRETS=true`. Breaking change documented in CHANGELOG. - Zed import 2-step is the new default; legacy 1-step preserved behind `OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP=true` and will be removed in v3.9. Closes #2863 * feat: implement automated skill workflows and update system configuration and validation schemas * test: eliminate dynamic cast warnings in cloud-sync unit test * test: isolate services-branch-hardening database directory to avoid concurrency issues * chore(docs): refresh generated docs collection index Update the generated Fumadocs browser collection mapping to keep documentation imports in sync with the current docs structure. * docs: update generated browser docs collection manifest Refresh the generated Fumadocs browser collection mapping so the docs site can resolve the current documentation files correctly. --------- Co-authored-by: OpenClaw <openclaw@kuzhomesrv.local> Co-authored-by: Dmitry Kuznetsov <139351986+dmitry@users.noreply.local> Co-authored-by: KuzyaBot <kuzya@local> Co-authored-by: JeferssonLemes <jeferssondev@gmail.com> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: Markus Hartung <mail@hartmark.se> Co-authored-by: akarray <akarray@users.noreply.github.com> Co-authored-by: Apostol Apostolov <theapoapostolov@gmail.com> Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com> Co-authored-by: Dmitry Kuznetsov <dmitry@kuznetsov.me> Co-authored-by: Nikolay Alafuzov <alafuzov_nn@rusklimat.ru> Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: Ronaldo Davi <alltomatos@users.noreply.github.com> Co-authored-by: levonk <277861+levonk@users.noreply.github.com> Co-authored-by: Lenine Júnior <lenine@engrene.com.br> Co-authored-by: Annas Alghoffar <aag.annas@gmail.com> Co-authored-by: Tushar Agarwal <76201310+Tushar49@users.noreply.github.com> Co-authored-by: GreatLiu <eurasiaxz@qq.com> Co-authored-by: yuna amelia <230527278+yunaamelia@users.noreply.github.com> Co-authored-by: Randi <55005611+rdself@users.noreply.github.com> Co-authored-by: Container <78986709+disonjer@users.noreply.github.com> Co-authored-by: nickwizard <35692452+nickwizard@users.noreply.github.com> Co-authored-by: Rajvardhan Patil <rajvardhanpatil7890@gmail.com> Co-authored-by: Raxxoor <manker_lol@hotmail.com> Co-authored-by: Muhammad Mugni Hadi <mugnimaestra3@gmail.com> Co-authored-by: mi <123757457+soyelmismo@users.noreply.github.com> Co-authored-by: Automation <automation@omniroute> |
||
|
|
b91ffa7f72 |
Release v3.8.4 (#2678)
* chore: bump version to 3.8.4 * feat(providers): enhance Google Gemini, CLI, and Antigravity resilience and features (#2676) Integrated into release/v3.8.4 * docs: add PR #2676 to changelog * fix(vision-bridge): process images when vision-capable model has combo mapping When a model-combo mapping routes a vision-capable model through a combo where some targets may NOT support vision, the vision bridge must process images so combo targets can describe them. Before: if body.model supports vision, the vision bridge skipped image processing entirely. Non-vision combo targets would receive raw images they can't handle. After: before skipping, check if the model has a model-combo mapping. If it does, process images through the vision bridge regardless of body.model's native vision support. - Add checkModelHasComboMapping() helper (dynamic import, failsafe) - Add checkModelHasComboMapping dep to VisionBridgeDependencies (testable) - Guardrail preCall: check combo mapping before early-return on vision support - Add VB-S11 / VB-S11b tests * fix(vision-bridge): only process images when some combo targets lack native vision Optimization per code review: instead of always processing images when a combo mapping exists, resolve the combo targets and check each target model's native vision support. Only invoke the vision bridge when at least one target model does not support vision. - Replace checkModelHasComboMapping() with shouldProcessImagesForComboModel() - When combo has ComboRefStep targets, conservatively process images - When all targets are model steps with native vision, skip processing - On errors, process images (conservative fail-safe) * fix(combos): repair context handoff ordering and add per-model timeout Root cause: recordSessionModelUsage was called BEFORE getLastSessionModel, so prevModel always matched the current modelStr — handoff summaries were never generated when auto-routing switched models. Fix: call getLastSessionModel first (captures actual previous model), generate handoff on mismatch, then record the new model for next time. Also: - ORDER BY id DESC in session_model_history query (deterministic vs used_at which has second-precision ties) - 30s per-model timeout for combo routing (default FETCH_TIMEOUT_MS is 600s, too long for combo fallback scenarios) * Revert "fix(combos): repair context handoff ordering and add per-model timeout" This reverts commit |
||
|
|
29b9f27919 |
fix(proxyFetch): retry once on undici dispatcher failure before native fallback (#2222)
Integrated into release/v3.8.0 |
||
|
|
0594b1d2ea |
fix(providers): correct pollinations requests and provider dashboard state
Update Pollinations request transformation to send the selected model and stream flag so requests match the active endpoint behavior. Align the ChatGPT TLS client with shared proxy resolution so dashboard proxy context is honored before falling back to environment settings. Also refresh provider display names across dashboard pages, correct the Claude extra-usage toggle messaging and visual state, and mark Pollinations as offering a free public endpoint. |
||
|
|
f53caa93b6 |
fix: Qoder PAT validation treats 500 error as bypass to avoid false negatives (#1391)
fix: proxy context correctly inherited during token refresh to avoid expiration loops (#1390) |
||
|
|
c8828b8a42 |
fix(build): unblock release build and settings state updates
Add targeted TypeScript annotations and module declarations to reduce type errors in open-sse services, executors, and shared utilities while temporarily disabling checking in legacy files that still need migration. Reset stale `.next/standalone` output before isolated builds so release artifacts are generated from a clean state. Update the dashboard proxy settings UI to bypass cached settings reads and immediately roll back debug mode when the PATCH request fails, which prevents stale data and inconsistent toggle state. |
||
|
|
fceb9d4145 |
fix(core): resolve runtime edge cases and TypeScript regressions
Tighten request and provider typing across the SSE pipeline to fix nullability and inference issues in Claude compatibility, wildcard routing, usage tracking, proxy fetch, and response sanitization. Address runtime edge cases by normalizing Bailian hosts, guarding TLS session creation, preserving custom provider base URLs, and using safer OAuth form param construction during token refresh flows. Update dashboard data path exports and usage stats typing, and align E2E/unit tests with paginated API responses, internal model sync auth, and current response payload shapes. |
||
|
|
a3d1dc6cf9 |
fix(api): support image-only models and authless search providers
Allow image generation requests to omit prompts for models that only accept image input, and validate required inputs from model metadata instead of enforcing a text prompt for every request. Treat authless search providers as executable with built-in defaults so SearXNG can run without stored credentials, including during provider auto-selection. Also align runtime support with Node.js 24 LTS, harden thinking tag compression and proxy wildcard matching, and update tests for the new route and runtime behavior. |
||
|
|
6e9b23c8e2 |
fix(providers): support batch testing for web, search, and audio
Add dedicated batch test modes for web-cookie, search, and audio providers in the dashboard, API route, and request validation so category-level testing targets the correct connections. Rename legacy qoder refresh and usage helpers from iflow to qoder for consistency, and tighten regex handling in response cleaning, thinking compression, and proxy matching to address edge cases and static analysis findings. Also update related tests, typing fixes, and README star history embeds. |
||
|
|
3843751c58 |
security: Resolve GitHub CodeQL scan alerts
- Fixed proxyFetch regex incomplete escape - Updated contextManager regex to avoid Polynomial ReDoS (using [^]*?) - Removed redundant incomplete sanitization replace in page.tsx - Fixed perplexity-web missing flags (i) in regex and used [^]*? - Renamed callLogArtifact sha256 to artifactHash to fix false positive password hash alert |
||
|
|
9e45baae58 |
chore(release): v3.6.6 — Stabilization (#1241)
* fix(streaming): #1211 greedy strip omniModel tags to prevent literal \n\n artifacts - Changed regex quantifier from ? to * in combo.ts, comboAgentMiddleware.ts, and contextHandoff.ts to greedily strip all JSON-escaped newline sequences surrounding <omniModel> tags in SSE streaming chunks - Added \r to the character class for cross-platform robustness - Fixed Playwright strict-mode violation in combo-unification.spec.ts - Bumped OpenAPI version and CHANGELOG to 3.6.6 * fix: 3 bugs found during issue triage (#1175, #1187/#1218, #1202) - fix(gemini): strip VS Code JSON Schema extensions from tool schemas (#1175) Add enumDescriptions, markdownDescription, markdownEnumDescriptions, enumItemLabels and tags to UNSUPPORTED_SCHEMA_CONSTRAINTS so the Gemini sanitizer removes them before forwarding. GitHub Copilot injects these non-standard fields into tool definitions, causing Gemini to reject with 'Unknown name enumDescriptions at functionDeclarations[n].parameters'. - fix(health-check): unwrap proxy config object before passing to getAccessToken (#1187 #1218) resolveProxyForConnection() returns { proxy, level, levelId } but the health check loop was passing the full wrapper to getAccessToken(), which expects the inner config object (.host, .port etc). The proxy dispatcher validated .host on the wrapper (undefined) and threw 'Context proxy host is required', silently marking every connection as unhealthy every sweep. Fix mirrors the pattern already used in chatHelpers.ts: proxyResult?.proxy || null. - fix(ui): debounce models.dev sync interval slider to save only on release (#1202) The slider's onChange fired updateInterval() on every drag tick, sending a PATCH per pixel of movement. Rapid API responses overwrote UI state mid-drag. Introduce draftIntervalHours for smooth visual feedback; the PATCH fires on onMouseUp / onBlur once the user releases the control. * fix(providers): update Xiaomi MiMo token-plan endpoints (#1238) Integrated into release/v3.6.6 * fix(cc-compatible): trim beta flags and preserve cache passthrough (#1230) Integrated into release/v3.6.6 * feat(memory+skills): full-featured memory & skills systems with tests (#1228) Integrated into release/v3.6.6 * fix: forward client x-initiator header to GitHub Copilot upstream (#1227) Integrated into release/v3.6.6 * feat(bailian-quota): add Alibaba Coding Plan quota monitoring (#1235) * fix: resolve v3.6.6 backlog bugs (#1206, #1211, #1220, #1231) - fix(core): #1206 inject startup guard against app/ and src/app/ conflict - fix(health): #1220 add HEALTHCHECK_STAGGER_MS to prevent token refresh bursting - fix(proxy): #1231 prioritize HTTP 429 over quota body heuristics - fix(sse): #1211 strip leading double-newlines in responses API stream * fix(tests): resolve memory migration and skills route pagination bugs from PR overlaps * docs: Update CHANGELOG.md with v3.6.6 features (#1182, #1165, #1177) * chore(release): bump version to 3.6.6 Update package versions for the electron app and open-sse package. Sync llm.txt metadata and feature headings with the 3.6.6 release. * feat(core): harden outbound provider calls and add cooldown retries Add guarded outbound fetch helpers with private/local URL blocking, controlled retries, timeout normalization, and route-level status propagation for provider validation and model discovery. Introduce cooldown-aware chat retries with configurable requestRetry and maxRetryIntervalSec settings, model-scoped cooldown responses, and improved rate-limit learning from headers and error bodies so short upstream lockouts can recover automatically. Also align Antigravity and Codex header handling, require API keys for Pollinations, validate web runtime env at startup, restore sanitized Gemini tool names in translated responses, and inject a synthetic Claude text block when upstream SSE completes empty. * feat(models): add glmt preset and hybrid token counting Introduce GLM Thinking as a first-class provider preset with shared GLM model metadata, pricing, usage sync, dashboard support, and provider request defaults for higher token budgets and longer timeouts. Use provider-side /messages/count_tokens when a Claude-compatible upstream supports it, while preserving estimated fallback behavior for missing models, missing credentials, and upstream failures. Also add startup seeding for default model aliases and normalize common cross-proxy model dialects so canonical slashful model ids do not get misrouted during resolution. * feat(api): add sync tokens and v1 websocket bridge Add dedicated sync token storage, issuance, revocation, and bundle download routes backed by stable config bundle versioning and ETag support. Expose the v1 websocket handshake route and custom Next server bridge so OpenAI-compatible websocket traffic can be upgraded and proxied through the dashboard and API bridge. Expand compliance auditing with structured metadata, pagination, request context, auth and provider credential events, and SSRF-blocked validation logging. * docs: Update all documentation for v3.6.6 - CHANGELOG: Add WebSocket bridge, GLM Thinking preset, safe outbound fetch/SSRF guard, cooldown-aware retries, compliance audit v2, model alias seeding, and all Internal Improvements for the 3 new commits - README: Expand v3.6.x highlights table with 10 new features; add SafeOutboundFetch, CooldownAwareRetry, SSRF guard, TPS metric, sync tokens, WebSocket bridge to Resilience/Observability/Deployment tables - ARCHITECTURE: Bump date; add new modules to executive summary, API routes, SSE core services, Auth/Security section; add SSRF/Outbound guard failure mode (section 6); expand module mapping - ENVIRONMENT: Add OMNIROUTE_CRYPT_KEY/OMNIROUTE_API_KEY_BASE64 legacy aliases, OUTBOUND_SSRF_GUARD_ENABLED, CODEX_CLIENT_VERSION, and REQUEST_RETRY/MAX_RETRY_INTERVAL_SEC cooldown retry settings - FEATURES: Add 6 new feature sections — V1 WebSocket Bridge, Sync Tokens & Config Bundle, GLM Thinking Preset, Safe Outbound Fetch & SSRF Guard, Cooldown-Aware Retries, Compliance Audit v2 * fix: use api64 for proxy test (#1255) Integrated into release/v3.6.6 — IPv6 proxy test fix * fix(page): update custom models section to include all providers #1200 (#1256) Integrated into release/v3.6.6 — Gemini custom model picker fix * fix: provide default client_id fallbacks to prevent broken OAuth requests (#1246) Integrated into release/v3.6.6 — OAuth client_id default fallbacks * fix: translate max_tokens/max_completion_tokens → max_output_tokens in Chat→Responses translator (#1245) Integrated into release/v3.6.6 — max_tokens → max_output_tokens Responses API translation + unit tests * feat(oauth): support cursor-agent CLI as Cursor credential source (#1258) Integrated into release/v3.6.6 — cursor-agent CLI credential source support * fix(cc-compatible): restore upstream SSE and correct stream/combo timeout behavior (#1257) Integrated into release/v3.6.6 — CC-compatible upstream SSE restore + stream timeout fix + README table repair * fix(cli-tools): resolve API key resolution and model mapping bugs in CLI tools (#1263) Integrated into release/v3.6.6 * feat(cli-tools): add Qwen Code CLI integration (#1266) Integrated into release/v3.6.6 * fix(i18n): add missing zh-CN translations and fix logger imports (#1269) Integrated into release/v3.6.6 * fix(i18n): add Chinese i18n support to dashboard components (#1274) Integrated into release/v3.6.6 * feat: update Pollinations to require API key, remove free tier flag (#1177) * feat: friendly error messages for crypto/encryption failures (#1165) * feat: add TPS (tokens per second) metric column to request logs (#1182) * feat: merge custom/imported models into filter list for all providers (#1191) * feat(fallback): Fix provider-profile-driven lockouts (#1267) This integrates rdself's unify-provider-profile-locks PR manually to handle structural conflicts. * fix(claude): proper Anthropic SDK integration (#1271) * fix(healthcheck): use correct proxy wrapper format for getAccessToken (#1272) * chore(release): v3.6.6 — skills registry stability fix + final integration * fix(auth): harden bootstrap auth and memory dashboard behavior Restrict unauthenticated writes to /api/settings/require-login to the initial bootstrap window while keeping read-only checks public. This prevents post-setup config changes without blocking first-run login setup, and the onboarding flow now logs in immediately after setting the password. Restore memory API filtering and pagination behavior by supporting q searches, honoring offset-based requests, and avoiding unrelated fallback results when FTS misses. Update dashboard stats fallback to use the response totals consistently. Package the MCP server with explicit file entries and add regression tests for bootstrap auth and memory route behavior * fix(codex): remove max_output_tokens from body for compatibility * chore(release): v3.6.6 — include PR 1274 fixes in changelog * chore: exclude additional build artifacts and internal directories from npm package distribution * fix: update Gemini OAuth test to match registry defaults + codex UI improvements * fix: restore .mjs refs for scripts/ in test imports after ts migration * fix: restore next.config.mjs ref in dev-origins test * fix: implement db migration safety checks and codex config format * fix: disable mass-migration abort during unit tests based on auto-backup flag * fix: update script regex in auto-update tests to use .mjs * feat: Add Perplexity Web (Session) provider (#1289) Integrated into release/v3.6.6 * fix(cli): resolve codex routing config parsing, standardize select model button positioning, and clarify oauth documentation * docs(changelog): record recent cli, provider, and test updates Document the latest fixes for Codex routing configuration parsing and Lobehub provider icon fallback behavior. Add the note that the remaining JavaScript test files were migrated to TypeScript ES modules to reflect the completed test stack transition. * chore(release): merge #1286 minor improvements manually to avoid testing conflict * chore(test): rename perplexity-web.test.mjs to .ts to maintain 100% TS codebase * chore(docs): update CHANGELOG.md for perplexity-web provider * fix(security): resolve CodeQL incomplete URL substring sanitization via URL parsing in test mocks * fix: integrate compressContext() into chatCore.ts request pipeline Proactively compress oversized contexts before sending to upstream providers, preventing context_length_exceeded errors. Compression triggers at 85% of model's context limit using the existing 3-layer compressContext() function. - Import compressContext, estimateTokens, getTokenLimit from contextManager - Add compression check after translation, before executor dispatch - Estimate tokens and compare against 85% threshold of model's context limit - Apply 3-layer compression (trim tools, compress thinking, purify history) - Log compression events with before/after token counts and layers applied - Audit compression events for observability - Add unit tests verifying integration behavior Closes #1290 * fix(tests): align reasoning expectations with GLM thinking structure * fix: prevent orphaned tool_result messages in purifyHistory() When purifyHistory() drops oldest messages to fit context window, it can split tool_use/tool_result pairs — keeping the tool_result but dropping the tool_use that initiated it. This causes upstream providers to reject the request with format errors. Add fixToolPairs() that runs after each purification pass to remove: - OpenAI format: orphaned role='tool' messages without matching tool_calls ID - Claude format: orphaned tool_result content blocks without matching tool_use ID Closes #1291 * fix(tests): supply tool_use in mock so it is not dropped * chore: convert remaining test to TypeScript * fix(tests): restore compatibility with compressContext threshold test after tsx migration * docs: finalize v3.6.6 release documentation * fix(core): finalize provider removal, type issues, and codex API key config * fix(dashboard): render Web/Cookie, Search, Audio provider sections and fix TypeScript errors * fix: increase MCP web_search timeout to 60s (#1278) * fix: route combo testing properly for embedding models (#1260) * fix: accumulate excluded accounts in combo fallback loop (#1233) * fix: strip leading whitespace and newlines from first streaming chunk (#1211) * docs: clarify VPS and Docker settings for OAuth credentials (#1204) * fix: return real retry-after for pipeline gates (#1301) Integrated into release/v3.6.6 — returns real Retry-After values from pipeline gates * feat: streaming semantic cache, Cursor auto-version detection, and call-log enhancements (#1296) Integrated into release/v3.6.6 — streaming semantic cache, Cursor auto-version detection, call-log cache_source tracking * feat(api): support more OpenAI types (image, embeddings, audio-transcriptions, audio-speech) (#1297) Integrated into release/v3.6.6 — adds embeddings, audio-transcriptions, audio-speech, and images-generations support for custom OpenAI-compatible providers, plus Pollinations image registry * deps: bump hono from 4.12.12 to 4.12.14 (#1302) Integrated into release/v3.6.6 * deps: bump hono from 4.12.12 to 4.12.14 (#1306) Integrated into release/v3.6.6 * chore: stabilization fixes for v3.6.6 (#1298, #1254, #59, CI) * fix(providers): match correct endpoint for Xiaomi MiMo, strip routing prefix for custom openai endpoints (#1303, #1261) * feat(storage): add database backup cleanup controls * chore(release): v3.6.6 — Final Stabilization Push * Backport call log storage refactor to release/v3.6.6 (#1307) Integrated into release/v3.6.6 * deps: update dompurify to 3.4.0 to resolve CVE-XYZ (#60) * test: disable sqlite auto backup in CI to resolve E2E timeout (#24481475058) * chore(docs): sync CHANGELOG for v3.6.6 with missing features and fixes * chore(release): prep v3.6.6 infrastructure and type safety fixes - Migrated legacy .mjs scripts to .ts (bin, prepublish, policies) - Resolved pre-commit strict lint (t11 budget) errors in combo.ts - Explicitly typed all TS bindings in pack-artifact policies - Updated package.json commands to run Node via tsx/esm internally - Hardened CI/CD with explicit node version 22.22.2 checks - Completed stage validations for v3.6.6 final release * chore: fix TS build errors and e2e timeouts in CI - Migrate nodeRuntimeSupport to TS interfaces avoiding implicit any - Increase visibility timeouts in skills-marketplace E2E test to 15s to bypass CI flakiness - Complete migration of .mjs scripts to .ts ensuring type safety * chore(release): sync package version 3.6.6 across workspaces * test(e2e): universally increase UI component visibility timeouts from 5s to 15s to bypass CI starvation * chore(build): inject baseUrl, paths, and types:node into MITM tsconfig within prepublish hook to fix missing types in CI check --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Jack <5443152+hijak@users.noreply.github.com> Co-authored-by: Randi <55005611+rdself@users.noreply.github.com> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: Samuel Cedric <ceds.sam@gmail.com> Co-authored-by: Max Garmash <max@37bytes.com> Co-authored-by: Markus Hartung <mail@hartmark.se> Co-authored-by: Gi99lin <74502520+Gi99lin@users.noreply.github.com> Co-authored-by: Payne <baboialex95@gmail.com> Co-authored-by: Benson K B <bensonkbmca@gmail.com> Co-authored-by: clousky2020 <33016567+clousky2020@users.noreply.github.com> Co-authored-by: Ravi Tharuma <25951435+RaviTharuma@users.noreply.github.com> Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: Hdsje <vovan877@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: xiaoge1688 <moyekongling@gmail.com> |
||
|
|
a2436bc50b |
fix(gemini): harden schema sanitizing and streaming defaults
Remove unsupported Gemini schema fields including vendor-prefixed `x-` properties so translated tool definitions avoid rejected keywords. Emit an empty `content` field with the initial assistant role delta to keep OpenAI-compatible streaming output consistent for clients that expect content on the first chunk. Also force undici's fetch whenever a dispatcher is provided and treat `onRequestStart` version mismatches as fatal instead of falling back to native fetch, preventing broken proxy requests under mixed undici versions. |
||
|
|
1442c47bbb |
chore(release): v3.5.6 — email masking, model toggle, OpenRouter registries & bug fixes (#1080)
* fix(minimax): switch auth from x-api-key to Authorization Bearer (#1076) Integrated into release/v3.5.6 — MiniMax auth fix with authHeader consistency normalization * feat(CI,i18n): autogenerate language files + Add missing strings (#1071) Integrated into release/v3.5.6 — i18n translations for memory, skills, and missing keys across 31 languages * fix(ci): restore i18n continue-on-error, remove auto-commit race condition * fix(husky): load nvm in hooks for VS Code compatibility * fix(husky): gracefully skip hooks when npm is not in PATH * fix: convert OpenAI function tool_choice to Claude tool format (#1072) * fix: prevent EPIPE feedback loop filling logs at GB/s (#1006) * fix: fallback to native fetch when undici dispatcher fails (#1054) * fix: improve Qoder PAT validation with actionable error messages (#966) - Add QODER_PERSONAL_ACCESS_TOKEN env var fallback for both validation and execution - Pre-flight ping check to diagnose connectivity issues (Docker/proxy) - Detect encrypted auth blobs from ~/.qoder/.auth/user and guide to website PAT - Clear error messages for auth failures with link to integrations page - Treat non-auth 4xx as auth-pass (request format issue, not token issue) - Update tests to cover new validation paths (23 tests, all passing) * feat: Improve the Chinese translation (#1079) Integrated into release/v3.5.6 * chore(release): v3.5.6 — i18n updates and credential security fixes * fix(ci): resolve e2e and docs-sync pipeline failures * fix(security): bump next to 16.2.3 to resolve SNYK-JS-NEXT-15954202 * fix: guard Memory/Cache UI against null toLocaleString crash (#1083) * fix: translate OpenAI tool_choice type 'function' to Claude 'tool' format (#1072) * fix: pass custom baseUrl in provider API key validation (#1078) * docs: update CHANGELOG with v3.5.6 bug fixes and security patches * docs: rewrite implement-features workflow with 5-phase harvest-research-report-plan-execute pipeline * docs: organize _ideia/ into viable/defer/notfit + add Phase 2.5 auto-response workflow * docs: implementation plans for #1025, #750, #960, #1046 + close already-implemented #833, #973, #982 * feat: mask email addresses in dashboard for privacy (#1025) * feat: add OpenRouter and GitHub to embedding/image provider registries (#960) * feat: add model visibility toggle and search filter to provider page (#750) * docs: move implemented features to notfit, update task plans status * chore: untrack _ideia/ and _tasks/ from git — private/internal only * chore(release): bump to v3.5.6 — changelog, docs, version sync & any-budget fix * fix: remove explicit .ts extension in qoderCli import that caused 500 error in production build --------- Co-authored-by: Jean Brito <jeanfbrito@gmail.com> Co-authored-by: zenobit <zenobit@disroot.org> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Ethan Hunt <136065060+only4copilot@users.noreply.github.com> |
||
|
|
7bd8ace1a2 |
fix(auth): remove fallback gemini oauth client secret
Stop shipping a default Gemini OAuth client secret and require it to come from the environment instead. Also tighten proxy fetch typing to avoid any-based undici calls and move db setup utilities under scripts while removing generated debug and scratch artifacts. |
||
|
|
4af7a1896c |
fix(proxy): use undici fetch to resolve Node 22 incompatibility with proxyDispatcher (#1056)
test: update login-bootstrap-route metadata assertions |
||
|
|
50f12869bf | fix: make streaming timeouts env-configurable | ||
|
|
c34b3f41bd | feat: Add requested model to logs, enhance background task detection, and introduce AI SDK compatibility utilities. | ||
|
|
baa0208fa9 | feat: v2.0.0 - MCP server, A2A agent, proxy improvements and docs update | ||
|
|
0d8f28a4a4 | feat: Introduce A2A lifecycle management, add type safety to ComfyUI and stream handling, and update various handlers and translators. | ||
|
|
71d14209a4 |
feat: OmniRoute v1.0.0 — Intelligent AI Gateway & Universal LLM Proxy
OmniRoute is an intelligent API gateway that unifies 20+ AI providers behind a single OpenAI-compatible endpoint. Features include intelligent routing with 6 strategies, multi-format translation (OpenAI/Claude/Gemini/Responses API), circuit breakers, semantic caching, combo fallback chains, real-time health monitoring, and a full dashboard with provider management, analytics, and CLI tool integration. Key highlights: - 20+ providers (Claude Code, Codex, Gemini CLI, GitHub Copilot, iFlow, Qwen, Kiro, etc.) - 6 routing strategies (Fill First, Round Robin, P2C, Random, Least Used, Cost Optimized) - Export/Import database backup with full archive support - Translator Playground with 4 modes (Playground, Chat Tester, Test Bench, Live Monitor) - 100% TypeScript across src/ and open-sse/ - Docker support with multi-stage builds - Comprehensive documentation and 9 dashboard screenshots |