mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 21:32:10 +03:00
ec0be07682ba3b3ea4617024c353e2aaaefe447f
73 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7cc922ba97 | fix(dashboard): preserve connection health visual on last routed topology node (#8428) | ||
|
|
58ab8b1d2c |
Clicking a provider card hitting back loses scroll position (#8349)
* clicking a provider card hitting back loses scroll position * clean up code * Rename some funcitons * minor UX updates * add null-guard in highlight() * Add providerCardHandle tests * add highlight tests. refactored code into separate utility functions * Minor change to skip a firstElementChild call - use a ref to access the Link inside ProviderCard. |
||
|
|
875de01de7 |
chore(quality): drop stale muse-spark-web allowlist entry + sync sidebar order snapshots (#8383)
Two independent "code is right, bookkeeping lagged" base-reds: 1. #8233 made open-sse/executors/muse-spark-web.ts import sanitizeErrorMessage from utils/error.ts (a real Rule #12 fix), but left its KNOWN_MISSING_ERROR_HELPER allowlist entry in scripts/check/check-error-helper.mjs in place. The gate's own stale-allowlist enforcement (assertNoStale) correctly flagged the now -obsolete entry: `npm run check:error-helper` failed with "1 entrada(s) obsoleta(s)", and tests/unit/check-error-helper.test.ts's "the shipped allowlist freezes exactly the known current violators" test expected an empty Set. Removed the entry (kept the assertNoStale machinery and the general scope-header comments untouched). 2. #8064 added the "compression-exclusions" sidebar item right after "compression-studio" in COMPRESSION_CONTEXT_GROUP (deliberate, complete feature) but didn't update two order-snapshot tests written before that item existed: - tests/unit/sidebar-visibility.test.ts expected the "omni-proxy" section's flattened id list to end the compression block at "compression-studio". - tests/unit/ui/sidebar-engine-items.test.ts asserted "Studio must be last" in COMPRESSION_CONTEXT_GROUP. Updated both to the real, intentional order: Settings -> Combos -> engines -> Studio -> Exclusions (Studio now second-to-last, Exclusions last). Validation (red -> green): - check:error-helper gate: red ("1 entrada(s) obsoleta(s)") -> green ("OK (898 files scanned, 0 known-missing frozen)") - tests/unit/check-error-helper.test.ts: 31/32 -> 32/32 - tests/unit/sidebar-visibility.test.ts: 6/7 -> 7/7 - tests/unit/ui/sidebar-engine-items.test.ts: 13/14 -> 14/14 Refs #8233 Refs #8064 |
||
|
|
2a865aaaa7 |
feat(settings): configurable model catalog cache TTL (#8219)
* feat(settings): configurable model catalog cache TTL Add modelCatalogCacheTtlMs to DatabaseSettings with default 1500ms. Extend cache-config API route to accept the new field. Replace hardcoded catalog cache TTL with dynamic settings value. Add 'Cache' settings tab at /dashboard/settings/cache with sidebar entry, i18n keys, header description, and legacy route redirect. * feat(combo): add model connection filter toggle to ModelSelectModal Adds a 'Show configured only' checkbox below the search bar in ModelSelectModal that filters each provider group's models through hasEligibleConnectionForModel. Toggle state persists in localStorage. - Import hasEligibleConnectionForModel from domain/connectionModelRules - showConfiguredOnly state + localStorage persistence - connectionFilteredGroups memo layered on filteredGroups - Renders both provider section and empty state from connectionFilteredGroups * test(combo): add connection filter toggle tests for ModelSelectModal Three test cases: (1) hide excluded models when toggle on, (2) show empty state when all models excluded, (3) drop provider group when all its models excluded. All 3 tests pass. * fix(settings): correct cache-config route import + add route/tab coverage The cache-config route imported get/update helpers from a nonexistent module (@/lib/localDb/databaseSettings) and called an undefined updateSettings() in PUT, crashing every request. Import the real databaseSettings module (matching the sibling database/route.ts convention) and call updateDatabaseSettings(); idempotencyWindowMs is routed through the flat @/lib/db/settings module instead, since that is where it is actually read at runtime (idempotencyLayer.ts, runtimeSettings.ts) — it was never part of the databaseSettings "cache" section type. Also fixes a dashboard-typecheck regression in ModelSelectModal.tsx: the new connection-filter toggle called hasEligibleConnectionForModel() with activeProviders entries typed too narrowly to include providerSpecificData, which real connection objects carry at runtime. Adds: - tests/unit/cache-config-route-8219.test.ts: GET/PUT resolve without crashing, modelCatalogCacheTtlMs and idempotencyWindowMs round-trip. - tests/unit/ui/cache-settings-tab-bounds-8219.test.tsx: CacheSettingsTab min/max TTL bounds (100ms/60000ms) gate the Save button and surface a validation message; in-bounds values PUT correctly. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(quality): rebaseline sections.ts for #8219 own-growth --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
e7f965c9cc |
fix(compression): persist Headroom minRows (set 5 and reload keeps 5) (#8058)
* fix(compression): persist Headroom minRows (set 5 and reload keeps 5) Fixes diegosouzapw/OmniRoute#8056. Headroom detail settings had a Save-looking form but EngineConfigPage only persisted aggressive/ultra via SETTINGS_SUBOBJECT, so minRows always reseeded to the schema default (8) after reload. - Add HeadroomConfig + DEFAULT_HEADROOM_CONFIG (minRows: 8) - Accept headroom in compressionSettingsUpdateSchema (minRows 2..10000) - Normalize/store headroom in get/updateCompressionSettings - Register headroom in EngineConfigPage SETTINGS_SUBOBJECT so Save works - Merge settings.headroom into stacked stepConfig for runtime apply - Thread minRows through preview API + EngineConfigPage preview payload - Tests: schema/DB round-trip, engine apply, stacked merge, UI Save→PUT 5 * chore(quality): rebaseline compression.ts + strategySelector.ts own-growth (#8056 headroom minRows) --------- Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> |
||
|
|
dbdc7daade |
feat(compression): per-model/endpoint compression exclusion filter (#8034) (#8064)
* feat(compression): per-model/endpoint compression exclusion filter (#8034) * chore(quality): rebaseline compression.ts own-growth 845->850 (#8034 exclusions persistence) --------- Co-authored-by: Probe Test <probe@example.com> |
||
|
|
7c08c0afea |
chore(dashboard): reframe Kimi partnership as "Open Source Friends" (#8117)
* chore(dashboard): reframe Kimi partnership as "Open Source Friends" Kimi (Moonshot AI) asked to frame the collaboration under an "Open Source Friends" narrative instead of "Official Sponsor". Adopt "Supported by our Open Source Friends" — it keeps the backing/support signal and the friendship warmth — with Kimi as the founding friend, listed first. The substance is unchanged: affiliate links, the transparency note, first-in-list placement and the banner display window all stay exactly as they were. Only the label moves. - README: section header "Sponsors" -> "Supported by our Open Source Friends"; badge "Official Supporter" -> "Founding Friend"; thank-you and support-line copy reworded; official K3 banner (public/sponsors/kimi-k3-banner.png) added full-width at the top of the section, linked with aff=omniroute. - ProviderCard: badge/tooltip fallback strings -> founding-friend wording. - i18n: kimiSponsorBanner.title, kimiOfficialSupporterBadge and kimiOfficialSupporterTooltip updated across all 43 locales (en + 42 translations), dropping the sponsor framing in every language. - Test providerCardKimiPartnerAccent aligned to the new "Founding Friend" badge. * docs(readme): add Sponsors honor-roll for financial backers Credit the project's GitHub Sponsors just below the Open Source Friends section. The two public sponsors (Professor Igor Morais Vasconcelos, longtao) are named with avatar links; the one sponsor who chose private visibility on GitHub Sponsors is credited anonymously, without exposing their identity. * docs(readme): generalize private-sponsor credit to 'and others' |
||
|
|
9020ed53f9 |
fix(grok-cli): require full auth.json on OAuth paste import (#7610) (#8027)
* fix(grok-cli): require full auth.json on OAuth paste import (#7610) The Grok Build paste path told operators to paste only the JWT "key" field, which creates connections with refresh_token=null that can never auto-refresh. Require the full ~/.grok/auth.json object (with refresh_token) in OAuthModal, and reject bare JWT pastes with a clear error. * fix(grok-cli): add behavioral test coverage for auth.json paste-import (#7610) Replace the source-regex-only test for the OAuth paste-import path with a real behavioral suite (bare JWT rejected, auth.json missing refresh_token rejected, multi-entry auth.json accepted, valid auth.json POSTed) using the existing grok-device-oauth-modal.test.tsx jsdom harness. Extract parseGrokCliPasteToken() into its own src/lib/oauth/utils/grokCliAuthJson.ts module so it is directly unit-testable and to keep OAuthModal.tsx's frozen file-size gate from growing (bump 1080->1100, justified in file-size-baseline.json, mirroring the existing extraction precedent on this file). Also fixes two pre-existing "JWT Token" label assertions that this PR's own tab rename ("Import auth.json") had left stale. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
e09b5d2527 |
feat: provider tab account search + mirrored top pagination (#7937) (#7968)
* feat: provider tab account search + mirrored top pagination (#7937) Two client-side UI improvements to the provider connections/accounts list (all data already loaded in memory; PAGE_SIZE=50): - Mirror the pagination bar ABOVE the list (previously bottom-only) in both the flat/untagged branch and the tagged/grouped branch. - Add a case-insensitive substring account search input (id/tag/name/email) to the left of the status filter pills, searching across ALL accounts (not just the current page), resetting pagination to page 0 on change. - Add pagination to the tagged/grouped view, which previously had none. New pure helper `connectionsSearchFilter.ts` keeps the substring matcher testable and out of the already-large ConnectionsListPanel.tsx. Closes #7937 * i18n(vi): add providers.accountSearchPlaceholder for locale parity (#7937) |
||
|
|
b861dd045a |
feat: browser login for Grok Build provider (#7013) (#7735)
* feat(oauth): add browser login for Grok Build provider (#7013) * feat(oauth): grok-build supports device_code AND browser-PKCE side-by-side (#7013) Reworks #7735 so the browser PKCE login is added ALONGSIDE the device_code flow (#7358) instead of replacing it; the OAuthModal lets the user pick either method. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
287802cf86 |
fix: repair pre-existing red gates on the release/v3.8.49 tip (#8055)
* fix(dashboard): resolve Kimi banner casing collision + shrink frozen test file (release tip) - Rename src/app/(dashboard)/dashboard/kimiSponsorBanner.ts to kimiSponsorBannerGate.ts so it no longer differs from KimiSponsorBanner.tsx only by the first letter's case (breaks next build on case-insensitive filesystems). Updates the sole importer (KimiSponsorBanner.tsx) and the two tests that reference it. - Extract the 8 Kimi/Moonshot featured-ordering tests out of the frozen tests/unit/providers-page-utils.test.ts (grown 3 lines past its 1294 cap by #8039's rebrand-comment update) into a new sibling file tests/unit/providers-page-utils-kimi.test.ts. No assertions dropped; both files pass in full (24 + 8 = 32 tests). * fix(sse): register PromptQlExecutor in the executor registry (release tip) getExecutor("promptql") had no entry in open-sse/executors/index.ts, so it silently fell through to DefaultExecutor's provider fallback, which issues a raw fetch() and returns the bare upstream Response instead of the executor wrapper shape {response, url, headers, transformedBody}. The real PromptQlExecutor class (open-sse/executors/promptql.ts) already honors the contract correctly — it was just never wired into the registry. Fixes tests/unit/executor-web-cookie-sweep.test.ts "promptql executor returns wrapper shape". * fix(i18n): backfill 2220 missing pt-BR keys to restore en.json parity (release tip) pt-BR.json fell behind after #7935 restored +2220 keys into en.json and vi.json but left pt-BR.json unmodified. Translated all missing entries to Brazilian Portuguese, preserving ICU/interpolation placeholders and existing terminology, and merged them mirroring en.json's key order so the diff is additions-only (the small comma-only deletions are pure JSON reformatting from new sibling keys). * fix(providers): repair 4 pre-existing catalog/registry reds on release tip - providers-constants-split.test.ts: APIKEY_PROVIDERS grew 182->187 (PR #7887 added 5 free-tier providers: ainative/aion/sealion/routeway/nara). Verified no dup/loss (6-family partition sums exactly to 187) and updated the stale expected count + comment trail to match. - cline registry: added the missing minimax/minimax-m3 free OpenRouter entry (#3321) and fixed the neighbouring nemotron-3-ultra-550b-a55b entry, which carried a stray ":free" id suffix and an imprecise 1_000_000 contextLength instead of the 1_048_576 the test (and every sibling 1M-context entry in this catalog) expects. - promptqlModels.ts / registry/promptql/index.ts: PROMPTQL_FALLBACK_MODELS's minimax-m3 entry was missing supportsVision, and the registry mapping dropped it entirely (only id/name were passed through) — it was the sole minimax-m3 entry across the whole registry not flagged multimodal, despite every other provider (minimax, minimax-cn, ollama-cloud, trae, bazaarlink, clinepass, codebuddy-cn, opencode-zen/go, synthetic, huggingchat, lmarena) agreeing MiniMax-M3 supports vision. Added the field to the PromptQlModel type and threaded it through. - tests/snapshots/provider/translate-path.json: regenerated the golden via UPDATE_GOLDEN=1. Diffed old vs new — zero providers removed, 5 added (ainative/aion/nara/routeway/sealion, matching #7887), and the only changed entry (cline) reflects the already-merged #7914 ClinePass header protocol change (Cline/<version> User-Agent + X-Task-ID) that a prior narrow golden touch-up missed capturing. * fix(docs): repair docs-sync/env-sync/repo-contract gates (release tip) Six pre-existing reds on release/v3.8.49, all "repo drifted from its own documented contract": - check-docs-counts-sync: free-tier headline was stale (~1.4B/~2.0B) vs the live catalog (~1.53B steady / ~2.15B first month, 43 pools). Updated README.md and docs/reference/FREE_TIERS.md to the live numbers and added a v3.8.49 correction note explaining the pool-count delta (39->43, #7840). Also fixed a soft executors-count drift in ARCHITECTURE.md (84->86, 268->271 providers) while touching that line. - release-green-docs-drift-7253: docs/proxy-subscriptions.md referenced a fabricated migration filename (123_proxy_subscriptions.sql); the real file is 131_proxy_subscriptions.sql. Fixed all 3 occurrences. - check-env-doc-sync + issue-7793-env-doc-sync-repro: OMNIROUTE_DATA_DIR (DATA_DIR fallback alias read by open-sse/executors/promptql/threadSticky.ts) was undocumented. Added to .env.example and docs/reference/ENVIRONMENT.md. - check-db-rules: src/lib/db/proxySubscriptions.ts (#7299) is a db-internal split of proxies.ts (kept under the frozen file-size cap) whose one export is already re-exported via proxies.ts -> localDb.ts. Added it to INTENTIONALLY_INTERNAL with the same db-internal justification used for identical split modules (apiKeyColumnFallbacks, providerNodeSelect, webSessionDedup) rather than a redundant direct re-export from localDb.ts. - mcp-server-hollow-dist-deps: the sanity test expected better-sqlite3 among the MCP bundle's static top-level external imports. That's been stale since the pre-#7878 migration to a cascading SqliteAdapter driver factory (createRequire()-based lazy require, not a static import); better-sqlite3 already has its own native-asset copy guarantee in assembleStandalone.mjs, unrelated to this test's EXTRA_MODULE_ENTRIES concern. Updated the assertion to a still-genuinely-static external (zod) with a comment explaining the change. No production runtime behavior changed — docs, .env.example, and a checker allowlist/test-expectation only. * fix(dashboard): repair stale UI component-shape test assertions (release tip) Two pre-existing reds in the dashboard UI component-contract cluster were caused by test assertions that had gone stale after intentional, correct refactors — not by real defects in the components: - quota-pool-wizard-multi.test.ts: the step-3 preview assertion required the literal single-line substring "connectionIds.map((cid)". Prettier (100-char width, project config) legitimately breaks the connectionIds.map(...).filter(...) chain across lines because of the multi-line callback body, so the literal never matches. PoolWizard.tsx still builds previewByProvider correctly by mapping over connectionIds; updated the assertion to a regex that tolerates the line break. - v388-phase1-screen-fixes.test.ts: the shared Select placeholder-guard assertion required the literal "!children && placeholder". An earlier, intentional i18n commit changed the hardcoded "Select an option" default to a translated fallback (`placeholder ?? t("selectOption")`), which requires parens around the ?? expression for operator precedence. The guard behavior is unchanged (still gated on !children); updated the assertion to match the current, correct guard shape. Both fixes are read-only test-file changes; no production behavior changed. review-reviews-v3814-fixes.test.ts still has one pre-existing, unrelated red (LEDGER-4: minimax-m3 registry entries missing supportsVision) that requires editing the promptql provider registry/catalog — out of this cluster's scope, left untouched and reported separately. * fix(providers): reconcile cline catalog contradictions + deterministic golden (release tip) The first tip-green pass introduced 3 regressions caught by CI on sibling guard tests: - clinepass-provider + cline-catalog-models-3321 encoded OPPOSITE expectations of the same cline model list (minimax presence, nvidia :free suffix). Reference upstream (OpenRouter free lineup) confirms nvidia/nemotron-3-ultra-550b-a55b:free (with :free, 1M ctx) is correct, so restore that id and fix #3321's stale no-:free assertion; add minimax/minimax-m3 (the real #3321 gap) to clinepass-provider's list. - check-db-rules-classification froze INTENTIONALLY_INTERNAL at 35; proxySubscriptions was the intentional 36th entry — add it + bump the count. - provider-translate-path golden stored a LITERAL Cline/3.8.49: clineAuth resolves the version from APP_CONFIG.version (stable), but the golden sanitizer collapsed only process.env.npm_package_version (unset under `node`, set under `npm run`) — so the golden was shard-dependent. Resolve APP_VERSION from APP_CONFIG.version like clineAuth and regenerate; now Cline/<APP> normalizes identically in every shard. * fix(services): type execFile signal/killed in classifyError + ratchet dashboard baseline (release tip) Pre-existing base-red on the tip's Fast Quality Gates (dashboard-typecheck), missed in the first inventory: - src/lib/services/installers/utils.ts TS2339 — `err.signal` was read off a value typed as NodeJS.ErrnoException, which @types/node does not declare `signal`/`killed` on (those belong to execFile's ExecFileException). Widen classifyError's param to type both, and drop the now-redundant `(err as … { killed })` cast. - Ratchet config/quality/dashboard-typecheck-baseline.json down: 5 baselined errors were fixed by already-merged PRs but never ratcheted (OAuthModal TS2769 4→3 / TS2345 4→3, CliproxyModelMappingEditor TS2339, CompressionPreviewAccordion TS4104, MonacoEditor TS2307). Baseline now 254, matching live — gate exits 0. |
||
|
|
f6d6ad6047 |
feat(dashboard): Kimi sponsor banner, Kimi Coding preset, official logomarks and partner links (#8039)
Official Kimi (Moonshot AI) partnership rollout on the dashboard: a dismissible home-page sponsor banner (version-gated through v3.8.60), a one-click "Kimi Coding" combo preset (kimi-k3 primary via moonshot, fallback to kimi-coding/kimi-web), the official theme-aware logomark wired into ProviderIcon and the README sponsors card, and aff-tagged partner links (aff=omniroute) across the 3 visible Kimi provider cards' top-of-page header link. The moonshot provider's dashboard display name is rebranded to "Kimi" (id/alias/routing untouched — DB connections, combos and /dashboard/providers/moonshot still address it by id). Refs: Kimi partnership pilot. |
||
|
|
4012bac41d |
fix(i18n): preserve remaining Vietnamese localization (#7935)
* fix(i18n): preserve remaining Vietnamese localization * chore(quality): rebaseline file-size cap for 9 dashboard components (i18n wiring) Restoring the Vietnamese localization on 9 dashboard components (useTranslations wiring + t()/tc() call-site swaps for previously hardcoded strings) grows each file by a small, irreducible amount. Bumps the frozen file-size-baseline.json caps to match, with a justification entry per the project's own ratchet policy. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(i18n): wire weekday localization + add missing qwen CLI description Two gaps left by this PR's own new contract tests, caught while reconciling the branch against the release tip: - CostOverviewTab.tsx added formatWeekdayLabel() but never called it; the Weekly Usage Pattern chart still showed raw English day abbreviations regardless of locale. Now maps weeklyPattern rows through it before handing them to WeeklyPatternCard. - cliTools.toolDescriptions was missing an entry for "qwen" (a baseUrlSupport:"full" tool) in both en.json and vi.json, failing the PR's own cli-catalog-display-contract.test.ts. Covered by the PR's existing tests/unit/dashboard-localization-contract.test.ts and tests/unit/cli-catalog-display-contract.test.ts (both now pass). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * i18n(vi): backfill the 4 proxySubscription keys #7299 added to en.json #7299 (proxy subscriptions) merged while this branch was rebasing, adding settings.proxySubscriptionsTab and settings.proxySubscription.error.{LOCAL_CORE_ENDPOINT_INVALID, NEEDS_CORE_NOT_CONFIGURED,NO_USABLE_NODES} to en.json. This PR's own i18n-vi-completeness contract asserts full en↔vi key parity, so the merge of the current release tip surfaced them as missing. Adds the Vietnamese translations, keeping the SS/VMess/Trojan/VLESS/SOCKS5 technical terms verbatim. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: nguyenha935 <208228297+nguyenha935@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: nguyenha935 <nguyenha935@users.noreply.github.com> |
||
|
|
387ebc3e41 | fix(dashboard): safely render structured error objects in Request Logs detail (#7845) (#7920) | ||
|
|
62cbbcd2c0 |
fix(dashboard): preserve quota cutoff drafts (#7909)
Co-authored-by: Bryan Nathan <bryan@users.noreply.github.com> |
||
|
|
0df0ff2ddb |
feat(grok-cli): align with official Grok Build client (#7358)
Rebuilt clean on release/v3.8.49 (branch forked from old main, ~drift). Resolved 2 real conflicts against the current tip: providerModelsConfig.ts keeps BOTH the tip's DashScope text-model helpers (#7882) and this PR's ProviderModelsHeaderContext type; OAuthModal.tsx takes this PR's DEVICE_CODE_PROVIDERS set (superset of the tip's hardcoded chain + grok-cli), dropping the now-dead qwen entry (#7866 removed qwen OAuth). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
00677044be |
[Part 3/3] feat(qwen): add regional Alibaba and Qwen Cloud providers (#7882)
* feat(qwen): add Qwen3.8 Max Preview catalogs [Part 2/3] Rebuilt clean on release/v3.8.49 after Part 1 (#7866) squash-merged — applies only the Part-2 delta (Qwen Web / Qoder qwen3.8-max-preview registration + required-thinking allowlist + Qoder client rework) onto the current tip. No migration in this part (that was Part 1). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * feat(qwen): add regional Alibaba and Qwen Cloud providers [Part 3/3] Rebuilt clean on top of Part 2 (#7874) over the current release tip — applies only the Part-3 delta (alibaba Model Studio, Alibaba Token Plan, qwen-cloud, qwen-cloud-token-plan with region selector). No migration in this part. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
d2ab1893ed |
feat(catalog): map unmapped free tiers, add navy + aihorde, surface keyless providers (#7840)
* feat(catalog): map unmapped free tiers, add navy + aihorde, surface keyless
Seven providers whose free tier was documented upstream but never reached our
catalog. Five of them we could already route — only the quota was missing.
Providers already routable, quota now mapped:
- requesty (200 req/day), ovhcloud (2 req/min per IP, anonymous), agnes
(permanently free), glm (GLM-4.7/4.5-Flash are Free on the official pricing
table). All registered as recurring-uncapped: their free tier is capped in
REQUESTS, not tokens, so inventing a token figure would inflate the headline.
The "~30M/month" that circulates for GLM belongs to BigModel.cn (a separate
Chinese offering) and is deliberately not recorded.
New providers:
- navy: one shared 150K tokens/day pool (~4.5M/month) drained by a per-model
token_multiplier. Registered as a SINGLE pooled row — summing its ~149 free
models would overcount ~149x.
- aihorde: crowdsourced volunteer GPUs, keyless via the documented anonymous
key. No tool calling and a 120s timeout, because requests queue for minutes.
Also:
- kilo-gateway reconciled against its live /models list (7 -> 13 models) and
flagged with the new trainsOnPrompts field: every free Kilo model reports
mayTrainOnYourPrompts: true, so the privacy cost now sits next to the quota.
- Free-tier page gains search, provider/keyless filters, per-row type badges,
a "no API key required" section and a curation-date freshness indicator.
- catalogUpdatedAt comes from an explicit FREE_CATALOG_CURATED_AT constant
rather than the data file's mtime: a standalone build rewrites timestamps on
deploy, which would advertise a months-old catalog as updated today.
Net effect on the headline: 462 -> 484 models but 1.371B -> 1.376B tokens,
because only navy publishes a token quota. That is the point — coverage grows
without the number lying.
* refactor(providers): derive one answer for "does this need an API key?"
"Works without a credential" lived in three registries that disagreed, and only
three providers were classified the same way in all of them:
- NOAUTH_PROVIDERS.noAuth -> whether the connect form hides the field
- RegistryEntry.authType / anonymousApiKey -> what the executor really sends
- FreeModelBudget.freeType === "keyless" -> how the catalog labels it
getCredentialRequirement() now derives the answer from the two sources that
describe real behaviour, returning none | optional | oauth | required. It adds
no list to maintain: registering a provider the usual way is enough. oauth is
deliberately NOT "works without a credential" — there is no key to paste, but
signing in is still a barrier, and calling it keyless would mislead.
anonymousApiKey outranks noAuth: AI Horde ships a documented anonymous key AND
honours a real one for higher queue priority, so it is "optional" rather than
"none" even though the form hides the field.
Fixes one real inconsistency this branch introduced: ovhcloud was catalogued as
keyless while its registry demanded a key. Verified live — the anonymous tier
answers /chat/completions with no Authorization header, and a BAD key returns
403 instead of degrading, so authType is now "optional" and the executor
attaches the header only when a real credential exists.
The 10 pre-existing divergences (agy, blackbox, pollinations, puter, qwen-web,
…) are frozen in KEYLESS_CATALOG_DRIFT with a stale-entry check: the gate blocks
new drift, and fails if a frozen entry stops drifting so the debt list cannot
outlive the debt. Resolving each one means confirming upstream behaviour, not
editing a list.
* fix(dashboard): build "no API key required" from routing, not freeType
Probing all ten providers the catalog labels `keyless` (2026-07-20) showed the
label answers a different question than the UI was asking:
blackbox 401 "No api key passed in."
friendliai 401 "no authorization info provided"
iflytek 401 Unauthorized
sparkdesk 401 Unauthorized
puter 401 "Missing authentication token"
muse-spark-web 403 (authHeader is a session cookie, not a key)
qwen-web 200 but serves the WAF HTML page, not the API
liquid 404 — endpoint moved; needs its own audit
pollinations 200 with real choices <- genuinely key-free
ovhcloud 200, and 403 on a BAD key <- fixed earlier in this branch
`freeType: "keyless"` means "free access not quantifiable in tokens" — it sits
beside `oauth` in FREE_TIERS.md for exactly that reason. The new section was
listing those rows under "No API key required", which would have sent users to
providers that reject them. It now derives from getCredentialRequirement().
pollinations was the one real find: it answers with no credential at all, so its
registry entry moves from apikey to optional and it leaves the recorded list.
The list is computed in the route handler, not the component: deriving it
client-side pulled the whole 201-entry provider REGISTRY into the browser
bundle. The component takes `noCredentialProviders` from the payload and stays
dumb — which is also why the vitest run could not resolve REGISTRY through the
`@omniroute/*` alias and silently classified every provider as credentialed.
* fix(test,providers): resolve open-sse in vitest; point liquid at its live host
vitest.config.ts / vitest.mcp.config.ts had no `@omniroute/open-sse` alias, so
imports from open-sse resolved to undefined instead of throwing. Tests stayed
green while every lookup silently returned a default — that is how the free-tier
card asserted on provider credentials with REGISTRY never loaded. Both configs
now mirror the tsconfig paths, and tests/unit/ui/open-sse-alias.test.tsx pins it
by asserting on values only reachable through REGISTRY (aihorde's anonymous key,
pollinations' optional auth), so a future regression fails loudly.
liquid pointed at api.liquid.ai, which stopped serving the API — every path now
returns a Vercel 404 HTML page, so routing failed with an unparseable body
instead of a clean error. The live OpenAI-compatible host is inference.liquid.ai
(403 {"detail":"Not authenticated"} without a key). Both verified 2026-07-20.
Swept every free-catalog provider for the same failure. Five more looked dead on
a /models probe (agentrouter, coze, kiro, nlpcloud, puter) but answer their chat
endpoint with real API JSON — a 404 on /models only means the path is not
exposed. They are untouched: liquid was the only genuine casualty.
* test(providers): move the APIKEY_PROVIDERS partition count to 180
This PR adds one gateway provider (navy), so the frozen entry-count and the
family-partition sum both shift by one. The assertions are moving targets by
design — they exist to catch a provider silently landing in two families or in
none, not to freeze the catalog size.
|
||
|
|
18a8da6df7 |
fix(dashboard): topology reflects connection health + clears finished requests (#7672)
The provider topology only lit nodes from live/recent traffic, so between requests (and right after a restart) it went blank even though 50+ connections were healthy — which reads as "lost providers". Two root causes: 1. Stuck-green latch: request.completed/request.failed are declared in the dashboard event map and consumed by useLiveRequests to drain the active-request set, but they were never emitted (only request.started was). A node's green "active" pulse therefore only cleared on a page reload, and accumulated over a session. Emit the terminal event from persistAttemptLogs — keyed by the same traceId as request.started — through a pure resolveRequestLifecycleEvent() helper (2xx/3xx + no error => completed, else failed). 2. No at-rest state: the map had nothing to show when idle. Colour each node by connection health (green connected / red error / grey idle) as a base layer, with live/recent traffic still taking precedence and pulsing brighter on top. edgeStyle() gains an optional trailing `healthy` param (static dim green) and StatusDot a `pulse` prop (static dot for connected-at-rest); both backward compatible. Legend "Active" -> "Connected". Tests: resolveRequestLifecycleEvent success/failure/token-alias units, edgeStyle healthy variant + precedence, and source guards for the emit wiring (traceId threaded into persistAttemptLogs) and the health-colour wiring. Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@users.noreply.github.com> |
||
|
|
2ea12ef6b7 |
[Emergency Fix] fix(build): repair release build blockers (#7772)
* fix(build): repair release build blockers * test(icons): cover Stepfun Mono fallback |
||
|
|
63716adc0b | feat(dashboard): show proxy name in badge, sort saved-proxy picker, default to Saved tab (#7643) (#7720) | ||
|
|
7a22f2d411 |
feat(dashboard): pin Kimi providers first in category + official supporter card accent (#7775)
* feat(dashboard): pin Kimi providers first in category + official supporter card accent Kimi (Moonshot AI) official-partnership highlight on the providers dashboard: Kimi-family providers (kimi-coding, kimi-web, moonshot) now render first within whichever category/group they already appear in, and their ProviderCard shows a Kimi-blue (#1783FF) accent border/glow plus an "Official Supporter" badge. Presentation-only — routing/fallback order is untouched. * docs(changelog): add fragment for Kimi provider card highlight (#7775) * feat(dashboard): strengthen Kimi card accent + prove featured-first per real section Owner refinement: make the official Kimi blue (#1783FF) border clearly legible (2px, higher opacity) and add a subtle whole-card tint alongside the existing glow, so the accent reads unmistakably as "the official Kimi color" in both light and dark theme, not just a faint hairline. Also adds concrete section-scoped regression tests against the REAL provider catalog (not synthetic mocks), mirroring page.tsx's exact category-building call chain, proving where each Kimi-family card actually lands today: - OAuth section -> kimi-coding first - Web Cookie section -> kimi-web first - API Key -> LLM subsection -> moonshot first (kimi-k3's home provider) - kimi-coding-apikey and kimi (both hiddenFromDashboard) never render as their own card in any section, confirmed across all 9 categories. |
||
|
|
53a6fea6fd |
Expose proxy controls for no-auth providers (#7419)
* fix(theoldllm): honor provider proxy for Vercel blocks * fix(theoldllm): fail closed when assigned proxy is unavailable * refactor(theoldllm): extract proxy guards into dedicated module * fix(ui): expose provider proxy for no-auth providers * fix(proxy): preserve provider-specific no-auth proxy routing * fix(ui): hide unsupported proxy control for VeoAI Free * fix(proxy): handle no-auth provider proxy assignments safely * fix(dns): expose agent-specific host resolution * fix(ui): gate no-auth proxy controls by provider capability * chore(ci): restart GitHub checks * fix(proxy): pass provider to no-auth count tokens resolution * chore(quality): rebaseline ProviderDetailPageClient 786->798 (3-PR irreducible wiring, campaign 2026-07-18) Three authorized PRs each add irreducible call-site wiring to ProviderDetailPageClient.tsx: #7360 +5 (ProviderQuotaVisibilityToggle render), #7419 +4 (NoAuthProviderControls wiring), #7062 +3 (Dahl provider hook) = 786->798. All three follow the extracted-component pattern; the frozen file only takes the wiring. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
12bf0ed077 |
fix(ui): improve React Flow dark theme (#7553)
* fix(ui): theme provider topology in dark mode * fix(i18n): isolate topology translation keys * refactor(i18n): reuse existing topology labels --------- Co-authored-by: nguyenha935 <208228297+nguyenha935@users.noreply.github.com> |
||
|
|
a7d08a43c3 |
fix(cli): refresh runtime detection accurately (#7552)
Co-authored-by: nguyenha935 <208228297+nguyenha935@users.noreply.github.com> |
||
|
|
a7dba3bbcc |
fix(dashboard): prefer public endpoint URLs (#7547)
* fix(dashboard): prefer public endpoint URLs * docs: add changelog fragment for #7547 * test(dashboard): cover onboarding public endpoint * refactor(hooks): split display-URL predicates below complexity gate Decompose isPrivateIpv4 and isPublicDisplayBaseUrl (both over the ESLint complexity gate of 15) into small named predicates. Behavior is unchanged: - isPrivateIpv4 now checks a PRIVATE_IPV4_RANGES table (RFC1918 + special-use ranges) through isInIpv4Range instead of one long chain of ||/&& comparisons. - isPublicDisplayBaseUrl now delegates to isSupportedProtocol, isLoopbackHostname, isMulticastDnsHostname and isNonPublicIpv6 (itself split into isIpv6LoopbackOrUnspecified / isIpv6UniqueLocal / isIpv6LinkLocal), preserving the isIpv6 gate so hostnames that merely start with "fc"/"fd" (e.g. fdroid.example.com) are not misclassified as IPv6 unique-local addresses. Adds IPv4 range-boundary and IPv6-gate regression tests; all existing assertions are unchanged. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: nguyenha935 <208228297+nguyenha935@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
c711ed257b |
Restore proxy navigation and sidebar accordion state (#7381)
* fix(sidebar): restore proxy navigation and accordion state * fix(proxy): prevent free pool translation crash * fix(settings): guard protected sidebar items and hydration state * test(sidebar): cover proxy visibility and expansion state * chore: restart pull request checks * refactor(sidebar): extract group item visibility control * refactor(sidebar): move group visibility control to module scope * fix(sidebar): preserve collapsed state on initial load * fix(proxy): collect free pool UI regression test * chore(test): unfreeze free-pool-tab.test.tsx from test-discovery baseline The move to tests/unit/ui/free-pool-tab.test.tsx (this branch) relinks it to the test:vitest:ui runner, so the tests/unit/free-pool-tab.test.tsx entry in the frozen orphan baseline is now stale and fails check:test-discovery. Removes the stale entry (60 -> 59 known orphans). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
c78f150ac3 |
feat(compression): support RTK TOML schema v1 filters (#7281)
* feat(compression): support RTK TOML filters * chore(ci): sync RTK skill and dependency allowlist * refactor(compression): reduce RTK import complexity * fix(i18n): add Portuguese RTK import translations * fix(compression): improve RTK TOML import validation feedback |
||
|
|
f287f42f15 |
perf: wrap ComboCard, HeroSection in React.memo (#7070)
* perf: wrap ComboCard, HeroSection in React.memo * fix(#7070): add test coverage for React.memo changes; fix selfref test in fork CI - Add smoke tests for combos page and EvalsTab to satisfy PR Test Policy requiring tests for production code changes - Fix selfref test (check-test-masking-selfref-6634) to try upstream/main first, falling back to origin/main, since origin/main may not exist in fork CI environments * fix(#7070): bump frozen baseline for combos/page.tsx 4655->4656 after React.memo wrapping The file-size checker's split('\n').length convention now counts 4656 for src/app/(dashboard)/dashboard/combos/page.tsx after wrapping ComboCard in React.memo (+1 effective line). --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> |
||
|
|
94509c0b5f |
feat: confirm before removing a single connection (#7361) (#7640)
Validated in merge-train 2026-07-18 @ 9084b408b: 12198/12201 pass; single red = earlyStreamKeepalive timer test, confirmed load-flake (6/6 green isolated on release tip AND the merged tree; no boarded PR touches keepalive) |
||
|
|
f5d705c277 |
feat(dashboard): in-product guidance for prompt compression engines (#7530) (#7634)
Validated in local merge-train @ 8f27177d1 (full parity suite green: typecheck+file-size+complexity+cognitive+changelog+unit shards 1&2+vitest) |
||
|
|
3cd04afe62 |
feat: add Type filter and easiest-first sort to Free Provider Rankings (#6915) (#7240)
* feat(dashboard): add Type filter and easiest-first sort to Free Provider Rankings (#6915) Adds sort/filter controls keyed on provider auth type (NOAUTH/OAUTH/APIKEY) to /dashboard/free-provider-rankings so zero-setup providers can be surfaced without eyeballing the Type column: - Type filter chips (All / No Signup / OAuth Login / API Key), client-side over the already-fetched rankings (no API change). - "Easiest first" sort toggle groups NOAUTH < OAUTH < APIKEY while preserving the existing score-descending order within each group (stable sort). - Type column legend/tooltip explaining what each auth type means. - Pure filter/sort logic extracted to a new freeProviderRankingsAuthType.ts module (no DB imports) rather than freeProviderRankings.ts, so importing it from the "use client" page does not pull server-only DB wiring (fs/path/better-sqlite3) into the client bundle; freeProviderRankings.ts re-exports both for API/test parity. - i18n keys added to en.json + __MISSING__ placeholders synced to all 42 locale mirrors (consistent with the existing untranslated-key convention). * test(6915): move page test to tests/unit/ui so a runner actually collects it The .test.tsx sat at tests/unit/ top-level, which no runner collects (vitest ui filters on tests/unit/ui) — check:test-discovery flagged it as a new orphan: the test never ran. Moved under tests/unit/ui/ and switched the dynamic import to the @/ alias (the convention of its neighbours). 6/6 now pass under test:vitest:ui. * fix(types): explicit unknown hop on the getProviderConnections cast (#6915) The dashboard-typecheck gate (added by #7203, after this branch was cut) scopes tsc to the src/app/(dashboard) import graph. This PR's page.tsx now imports freeProviderRankingsAuthType, which type-imports freeProviderRankings — dragging that module into the graph for the first time and surfacing its pre-existing TS2352 (JsonRecord[] -> ConnectionState[] is a structural subset). Fixed the cast rather than widening the frozen baseline. |
||
|
|
8143d8e3a6 |
feat: replace free-text model inputs with hidePaid-aware Selects (#6540) (#7229)
* feat(dashboard): replace free-text model inputs with hidePaid-aware Selects (#6540) Swap RoutingTab.webSearchRouteModel, ComboDefaultsTab.handoffModel, and BackgroundDegradationTab's from/to fields from free-text inputs to a new shared ModelSelectField fed by the already hidePaidModels-aware GET /api/models, with an off-catalog "(custom)" fallback so an existing saved value is never silently dropped. ModelRoutingSection's glob pattern field gets a fail-open "matches only paid models" warning instead, since it's a wildcard matcher rather than a single model id. Adds save-time paid-target rejection (PAID_MODEL_TARGET_BLOCKED, 400) on PATCH /api/settings, PATCH /api/settings/combo-defaults, and PUT /api/settings/background-degradation when hidePaidModels is on, failing open for aliases/combo names/unrecognized providers. globToRegex is extracted from lib/db/modelComboMappings.ts into a new dependency-free shared/utils/globPattern.ts so both the DB module and the new client-side pattern heuristic reuse the same regex-building logic. * refactor(6540): extract paid-target guard in background-degradation PUT The inline hidePaid check nested if>if>for>if inside the PUT handler, pushing its cognitive complexity to 16 (>15) — a NEW violation that broke the check:complexity-ratchets gate (891 > baseline 890). Extracted the check into a module-local hasBlockedPaidTarget() helper; behaviour and response body are unchanged. cognitive-complexity back to 890 = baseline. * fix(i18n): mirror paidModelPatternWarning into pt-BR.json (#6540) The new key landed only in en.json; the i18n pt-BR integrity test (no drift, #6695) requires pt-BR.json to carry every en.json key. |
||
|
|
0f10225f1d |
feat(dashboard): add compression-mode selector to Context & Cache combos page (#6760) (#7219)
Extracts the routing-combo compression-mode dropdown (Default/Off/Lite/
Standard/Aggressive/Ultra) from the combo card into a shared
ComboCompressionModeSelect component, reused on both the combo card
(compact) and the Compression Combos page's "Assign to routing" list
under Context & Cache. Both surfaces persist through the existing
PUT /api/combos/{id} route -- no backend or schema change.
|
||
|
|
63c85ea76d |
fix(dashboard): resolve costs page 500 from out-of-scope t() in TopListCard (#7272) (#7564)
* fix(dashboard): resolve costs page 500 from out-of-scope t() in TopListCard (#7272) TopListCard (CostOverviewTab.tsx) referenced the bare identifier `t` from an outer component's scope when rendering the zero-cost / !hasCostData branch, throwing "ReferenceError: t is not defined" and crashing /dashboard/costs?range=all&apiKeyIds=...&groupBy=model whenever a filtered slice landed only $0-cost rows. Extracted TopListCard into its own component file and threaded the resolved legacyFreeLabel string in as a prop, mirroring the existing CostBreakdownTable pattern in the same file. Also fixes a case of the typecheck:core dashboard .tsx coverage gap tracked in #7033. * test(dashboard): move TopListCard #7272 regression test to vitest UI project The node:test unit runner cannot load TopListCard's import chain (@/shared/components -> ProviderIcon -> @lobehub/icons ESM), which made the "Impacted unit tests (TIA subset; blocking)" CI job red with "Unexpected token 'export'" on tests/unit/costs-toplistcard-legacy-free-label-7272.test.ts. Moved the regression test to tests/unit/ui/ and rewrote it against the vitest UI project (test:vitest:ui, blocking in the test-vitest CI job), which handles the ESM import chain natively. Verified the test still fails with "ReferenceError: t is not defined" against the pre-#7272 TopListCard body and passes against the fixed component. |
||
|
|
d6df9314b4 |
fix: filter hidden custom models out of legacy combo model picker (#7156) (#7199)
* fix: filter hidden custom models out of legacy combo model picker (#7156) * chore(test): move model-select-modal-hidden-models-7156 test into tests/unit/ui (collector coverage) (#7156) |
||
|
|
3a92236d7a | fix: wire modelAliases fetch into HermesAgentToolCard (#7151) (#7195) | ||
|
|
a5cad5ab2a |
fix(tests): vitest UI suite back to green (69 fails triaged — WS6.1) (#7127)
test:vitest:ui was advisory/parked with 70 failing tests across 30 files (of 159 total). Triaged by grouping failures by root cause instead of fixing one-by-one: - 15 files (use-virtual-list, use-traffic-stream, use-system-proxy-exit-guard, use-session-recorder, use-resizable-panels, traffic-inspector-page, timing-i18n, stats-tab, session-recorder-bar, same-context-filter, historic-session-banner, conversation-tab, conversation-tab-separators, cli-tools-no-mitm-tab, agent-bridge-server-card-a11y) were authored against node:test but live under tests/unit/ui/*.test.tsx, which vitest.config.ts collects but test:unit's glob (only *.test.ts) never does — orphaned. Fixed by switching their describe/it/beforeEach imports to "vitest". - jsdom does not implement window.matchMedia, and several dashboard components read it via useTheme() (directly, or transitively through ProviderIcon). Added tests/_setup/vitestUiPolyfills.ts (wired into vitest.config.ts) with a minimal MediaQueryList polyfill — fixed providerCascadeNode, ProviderIcon-icon-url, CliAgentsPage, playground-studio, comboLiveStudio, memories-tab, home-topology-hidden, ProxyRegistryManager-tdz. - playground-build-tab.test.tsx (9 tests) and compressionHub*.test.tsx (2 tests) asserted against pre-redesign UI: BuildTab now sits behind a 3-step BuildWizard (mode picker -> configure -> run), and CompressionHub is a Phase-2 thin overview without the old master toggle/mode selector/pipeline list. Rewrote the build-tab test to drive the wizard, and removed the two compressionHub.test.tsx assertions already superseded by compressionHub-active-selector.test.tsx. compressionHub-context-editing.test.tsx asserted stale Portuguese copy against a component that deliberately uses literal English strings (documented hydration workaround) — aligned to the real text. - search-tools-compare-tab.test.tsx: the D22 4-provider cap documented in docs/frameworks/SEARCH_TOOLS_STUDIO.md was never implemented in CompareTab — fixed the component (disable extra toggles + cap selectAll + warning message) since the test was correct and the component was the bug. Also fixed an assertion looking for a <table> that never existed (the results panel is a div-based side-by-side layout). - CliAgentsPage.test.tsx: the agent-tool catalog grew from 6 to 8 (omp, letta added) since the test was written — updated the fixture and expected count. - memories-tab.test.tsx: a call-order-dependent fetch mock (mockResolvedValueOnce + fallback) broke once MemoriesTab started firing an immediate health check that raced its 300ms-debounced list fetch — switched to a URL-keyed mock like the rest of the file. - home-topology-hidden-4596.test.tsx: useLiveDashboard now runs an async handshake fetch before opening the WebSocket — stubbed fetch and awaited it. - same-context-filter.test.tsx: the filter branch moved from useTrafficStream.applyFilter into the extracted, reusable matchesTrafficFilter() helper — updated the source-grep target. - tests/unit/ui/provider-plan-config.test.tsx deleted: it tested ProviderPlanConfigClient, which tests/unit/quota-plans-route-retired.test.ts proves was deliberately retired (Plans screen removed). Result: test:vitest:ui 158/158 files, 870/870 tests passing (was 30 failed / 159, 70 failed / 743). test:vitest (MCP/autoCombo) still green at 28/28, 253/253. Not promoted to blocking in this PR per the task — the owner promotes after reviewing the green suite. |
||
|
|
14c182ff37 |
fix(dashboard): logs detail modal no longer reopens on first close (#6830)
LogsPage recomputed initialId from window.location on every render, but the App Router syncs window.location only after the navigation commits. Closing the detail modal re-rendered the page while the URL still carried ?id=X, so initialSelectedId flipped null -> X and the child's one-shot deep-link effect (guard still unarmed after the open-click render, where location was stale in the other direction) reopened the modal. Only the second close worked. Read the id once via lazy useState so the prop stays stable for the page's lifetime; deep links still open the modal on mount. Regression test reproduces the App Router ordering with a router.replace mock that re-renders the page before committing the URL. |
||
|
|
c92bdd13c6 |
fix(lmarena): modernize Arena web provider + static Direct-chat catalog (#6280)
* fix(lmarena): modernize Arena web provider + static Direct-chat catalog Update the lmarena provider for arena.ai (product rebranded from LMArena): - Route chat via arena.ai create-evaluation with Chrome TLS impersonation (tls-client-node) and optional browser-minted recaptchaV3Token. - Seed Text+Search (48) into the chat registry; seed Image (27) only into IMAGE_PROVIDERS. Disable live HTML model discovery; resolve public names to Arena UUIDs from the static TypeScript allowlist (no scrape JSON in-repo). - Soft-exclude 404/502 model ids; slow/stop bulk test-all probes for this provider. - Do not fold IMAGE_PROVIDERS/video specialty into the chat provider catalog when a chat registry already exists (lmarena/openai/xai). - Display name Arena (Free); keep wire id `lmarena` / alias `lma` for back-compat. - Theme-aware provider icons: arena-light.svg / arena-dark.svg. - Preserve split Supabase SSR cookie reconstruction for arena-auth-prod-v1.*. * fix(providers): align provider-models-route test fixture + regen provider reference Fold the topaz image-only catalog entry's apiFormat/supportedEndpoints into the local-catalog test fixture (route now tags media-only providers per the lmarena PR's staticModels.ts change), regenerate PROVIDER_REFERENCE.md against the merged release providers.ts, and add the changelog fragment for #6280. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test: align web-cookie fallback suite — lmarena now has a registry entry (probe path) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
65890d4aab |
fix(logs): prevent stale detail refresh reopening modal (#6323)
* fix(logs): prevent stale detail refresh reopening modal * chore(stryker): register ollama-quota covering tests (release drift from merge burst) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
e8fce67e70 |
feat(dashboard): add search to Playground model picker dropdown (#4086) (#6811)
* feat(dashboard): add search to Playground model picker dropdown (#4086) The shared ModelSelectModal (combo builder + CLI-code cards) already had search, but the Playground's raw model <select> in StudioConfigPane stayed a flat unsearchable list - unusable once a provider like OpenRouter contributed 50+ models. Adds a search input above the dropdown that filters options via filterModelsByQuery() (Turkish-safe accent/case-insensitive match, reusing matchesSearch()). The currently selected model always stays pinned in the list even when it doesn't match the query, so typing never silently swaps the active selection. Reuses the existing common.search i18n key already translated in all 42 locales - no new key needed. * chore(6811): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) |
||
|
|
a5c555b0de |
feat(icons): prioritize local SVG icons over LobeHub npm for faster rendering (#6317)
* feat(icons): prioritize local SVG icons over LobeHub npm for faster rendering * docs(changelog): add #6317 local-icons New Features bullet --------- Co-authored-by: hamsa0x7 <hamsa0x7@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> |
||
|
|
fd7e4c10e5 |
feat(compression): omniglyph engine (context-as-image, Fable 5 direct) — stack + single mode (#6556)
* feat(compression): dependência omniglyph (file:) + smoke de import * feat(compression): engine omniglyph — contexto-como-imagem com gates fail-closed * fix(compression): omniglyph adapter fail-open no transform (try/catch) * feat(compression): registra omniglyph no registry e catálogo (single mode, stackPriority 90) * feat(compression): modo único omniglyph (async), selecionar o modo é o enable * feat(compression): plumbing supportsVision + providerTransport até os engines * feat(compression): estimador de tokens image-aware — modo stacked mantém a saída do omniglyph * docs(compression): corrige comentário do prefixo base64 no decode PNG (64 chars) * feat(compression): registra omniglyph nas listas de modo/engine (db, combo, deriveDefaultPlan, mcp) * feat(dashboard): dedicated OmniGlyph engine screen (context-as-image) Adds a per-engine detail page at /dashboard/context/omniglyph, alongside the other compression engines in the sidebar. Four sections: the economics (measured savings), a REAL before→after (dense text vs the rendered PNG page, not a mockup), the fail-closed gate flow, and the enable control wired to /api/settings/compression (preview engine, off by default). Sidebar entry + i18n label across all locales. * chore(compression): consume published omniglyph@^1.0.0 from the npm registry Replaces the local file: dependency used during the preview phase — npm ci now resolves omniglyph from the registry with integrity, unblocking CI. * fix(compression): satisfy v3.8.47 quality gates for the omniglyph engine - dependency-allowlist: approve omniglyph (own package, published from diegosouzapw/OmniGlyph; supply-chain review done by the maintainer) - ladder maps (#6533 guard): rank omniglyph 80 (stackPriority 90, runs after every text engine) with expectedReductionFactor 0.35 (measured 0.23-0.33) - drop the two explicit any casts in omniglyph tests (no-explicit-any is error-level in tests since #6218) * chore(compression): rebaseline strategySelector for the omniglyph mode dispatch +18 lines of cohesive dispatch/type wiring at the existing mode chokepoints (sync no-op + async single-mode branch + providerTransport on the options types) — not extractable without hiding the dispatch boundary, mirroring the prior compression rebaselines. Also drops an unused eslint-disable directive in image-aware-tokens.test.ts (warning-level red under --max-warnings 0). * chore(quality): register inherited base tests in stryker tap.testFiles masked-200-exhaustion-fallback-6427 and headroom-codex-quota-snapshot-6379 arrived via the base merge without their stryker registration — check:mutation-test-coverage --strict requires covering tests to be listed. * refactor(compression): keep omniglyph wiring under the complexity gate - extract the async single-mode resolution to engines/omniglyphSingleMode.ts (runCompressionAsync was at complexity 17 after the mode branch; back <=15) - split OmniglyphContextPageClient into section components (was 161 lines in one function; every function now under the 80-line cap) - complexity baseline 2052->2053: the +1 is inherited base drift (the ratchet does not run on fast-path merges — same pattern as the v3.8.44/46 rebaselines); this PR's own code is measured complexity-net-zero * chore(quality): register 3 more inherited base tests in stryker tap.testFiles route-guard-middleware-local-only, combo-diagnostics-trace and idempotency-fusion-collision arrived via the latest base merge without their stryker registration (fast-path merges skip check:mutation-test-coverage). * chore(quality): cognitive-complexity baseline 883->884 (inherited base drift) check:cognitive-complexity measures 884 identically on the pristine origin/release/v3.8.47 tip and on this HEAD — the PR itself is cognitive-net-zero (single-mode resolution extracted to its own module, page client split into section components). Same inherited-drift pattern as the v3.8.4x release rebaselines. --------- Co-authored-by: diegosouzapw <diegosouzapw@devbox.local> |
||
|
|
ebdfe727a6 |
fix(test): replace tautology in playground-api-tab + make test-masking catch it (#6404) (#6603)
playground-api-tab.test.tsx's SSE test always took the disabled-button branch (the fetch mock returned an empty model list) and asserted a tautology instead of exercising the SSE path it claims to verify. The test now selects a real model to enable Send, asserts it is actually enabled, and asserts the streamed SSE content reached the response editor. check-test-masking.mjs's tautology subcheck only compares base-vs-HEAD counts within a PR's own diff and no-ops entirely outside PR context (no GITHUB_BASE_SHA/REF) -- so a tautology merged once, or checked with a bare local run, stayed invisible forever after. Added an always-on absolute-floor scan (scanBareTautologies/countBareTautologies) over every tracked test file, scoped to the bare expect(true).toBe(true)/assert.equal(1,1) patterns that have zero legitimate uses in this codebase -- deliberately excluding assert.ok(true), which has ~15 pre-existing verified-legitimate try/catch-fallback uses and stays on the lenient diff-only path. |
||
|
|
6d649d3d3f | fix(dashboard): size the web-session cookie modal to fit on 1080p (#6265) (#6601) | ||
|
|
18d0659426 |
fix(tests): replace expect(true) tautology in playground-api-tab (#6404) (#6548)
fix(tests): replace expect(true) tautology in playground-api-tab (#6404) Integrated into release/v3.8.47. (thanks @chirag127) |
||
|
|
1bda6c15dc |
Release v3.8.44 (#5925)
* fix(install): add pnpm-workspace.yaml allowBuilds + pnpm.json for pnpm 11+ pnpm 11 introduced ERR_PNPM_IGNORED_BUILDS for native addon packages. Without explicit allowBuilds approval, these packages silently skip build scripts and OmniRoute fails to start with missing native modules. Changes: - pnpm-workspace.yaml: Set allowBuilds=true for all 13 native addon packages (@parcel/watcher, @swc/core, better-sqlite3, core-js, esbuild, keytar, koffi, libxmljs2, onnxruntime-node, protobufjs, sharp, tls-client-node, unrs-resolver) - pnpm.json: Migrate onlyBuiltDependencies from package.json (deprecated field) to the new pnpm.json config file per pnpm 11 spec. Tested on: pnpm 11.9.0, Node 24, Windows 11. Fixes: pnpm install ERR_PNPM_IGNORED_BUILDS on fresh clone with pnpm 11. * chore(release): open v3.8.44 development cycle * test(security): parse Kimi Web URL host instead of substring match (CodeQL #689) (#5928) Alert js/incomplete-url-substring-sanitization: the Kimi Web executor test asserted result.url.includes("www.kimi.com"), which a hostile host like www.kimi.com.evil.net would also satisfy. Parse the URL and assert on the exact hostname (new URL(result.url).hostname === "www.kimi.com"), which is both a stronger check and clears the CodeQL warning. * refactor(translator): extract thinking-budget fitting from openai-to-claude (#5932) Extract the thinking-budget fitting cluster (fitThinkingToMaxTokens + private safeCapMaxOutputTokens + MIN_* constants) verbatim into the pure leaf openai-to-claude/thinkingBudget.ts. Host re-exports fitThinkingToMaxTokens so external importers keep working and imports it back for internal use. Host 822 -> 738 LOC (under the 800 cap). No behavior change: byte-identical bodies, public export set unchanged. Adds a split-guard test; all consumer tests stay green (translator-openai-to-claude, strip-empty, minimax-m3, passthrough). * chore(release): pipeline hardening — test-masking pre-flight gate + contributors/uncovered helpers (#5926) * chore(ci): add test-masking PR-context gate to release-green pre-flight Reproduce check:test-masking (vs origin/main) inside validate-release-green so non-allowlisted net-assert reductions surface in the local pre-flight instead of in a ~40-min CI layer on the release PR. run() now merges a per-gate opts.env so GITHUB_BASE_REF reaches the child. HARD gate; skipped under --quick. Context: v3.8.43 release cost 3 CI round-trips for PR-context gates (test-masking, file-size, pr-evidence) that check:release-green did not reproduce locally. * chore(release): add contributors generator + uncovered-commit reconciliation helpers - scripts/release/gen-contributors.mjs: reproducible `### 🙌 Contributors` table for a CHANGELOG version (parenthetical-group parser → accurate per-PR attribution, noise-handle denylist). v3.8.43 shipped without the section (a real miss) because it was hand-built. npm run release:contributors <version> [--inject]. - scripts/release/list-uncovered-commits.mjs: lists commits since the last tag with no CHANGELOG bullet (v3.8.43 had 123/176 uncovered at reconciliation start). Advisory, maintainer-side. npm run release:uncovered. - 20 unit tests (parenthetical attribution, noise exclusion, idempotent injection, coverage window). * chore(quality): absorb web-cookie-providers-new file-size drift from #5928 (base-red on release/v3.8.44) * refactor(translator): split openai-responses request translator into pure leaves (#5940) Extract the shared pure primitives and the chat->Responses direction out of the 894-line openai-responses.ts request translator: - openai-responses/helpers.ts: pure primitives (toRecord/toString/clampCallId/ normalizeVerbosity/etc + markers/regexes/JsonRecord), zero host imports - openai-responses/toResponses.ts: openaiToOpenAIResponsesRequest (chat->Responses), imports the helpers leaf Host keeps openaiResponsesToOpenAIRequest (Responses->chat, imported by production) plus both register() directions, and re-exports openaiToOpenAIResponsesRequest so external importers (tests) keep working. Host 894 -> 529 LOC (under the 800 cap). Verbatim bodies (multiset check: leaf A 54/54, leaf B 294 lines, fn1 intact), public export set unchanged, leaves never import the host (no cycle). Adds a split-guard test; all consumer tests stay green (responses-translation-fixes 37, verbosity 4, reasoning-effort 4, orphaned-tool-filter 8, empty-tool-name-loop 8, headroom-responses-format 3). * chore(ci): pr-evidence FAIL output tells you to push (body edit does not re-run the gate) (#5944) ci.yml ignores the 'edited' event, so adding the Evidence block to the PR body after a push does not re-run check:pr-evidence — you need another commit. The FAIL report now says so, at the exact place someone sees the red check. + 5 unit tests (classification + hint-on-fail / no-hint-on-pass). Decided against a separate edited-triggered workflow: pr-evidence is not a required check (no ruleset gates it; release PRs merge UNSTABLE, not BLOCKED), so the gap is cosmetic and the generate-release skill already puts Evidence in the body before the first push. * fix(providers): Perplexity Web emits real tool_calls in streaming mode (mirror chatgpt-web toolMode) (#5927) (#5937) Perplexity Web (Pro/Max) only converted <tool>{...}</tool> text into OpenAI tool_calls for non-streaming requests (hasTools && !stream). Streaming requests -- the default for agentic coding clients -- got the raw <tool> text as plain delta.content and never emitted a tool_calls SSE delta, so clients could not execute tools. Reuses the provider-agnostic buildToolModeResponse()/ toolCompletionToSseStream() helpers already shipped for chatgpt-web (#5240): when tools are requested, buffer the full completion and convert it into either a JSON completion or a terminal SSE replay carrying delta.tool_calls + finish_reason: tool_calls, regardless of the caller's stream flag. Extended buildToolModeResponse()'s idSeed to be caller-supplied (default 'cgpt', perplexity-web passes 'pplx') so tool_call ids stay provider-specific without duplicating the helper. Non-tool streaming is unchanged (still lives token-by-token via buildStreamingResponse). * fix(discovery): resolve duplicate /v1 paths and redirect aborts (#5904) Integrated into release/v3.8.44. Thanks @hamsa0x7 for diagnosing the doubled /v1 discovery path and the REDIRECT_BLOCKED probe-loop abort (#5899). De-scoped to the discovery fix (the #5903 session-affinity work is handled by #5943) and added Rule #18 regression guards. * docs(changelog): record #5926 + #5944 (release-pipeline hardening) under v3.8.44 Maintenance (#5952) * docs(claude): add Hard Rule #22 — cross-session safety (git stash + in-flight PRs) (#5955) Integrated into release/v3.8.44 — Hard Rule #22 (cross-session safety). * refactor(translator): extract pure helpers from response/openai-responses (#5949) Extract the 5 stateless helpers (normalizeToolName, stripEmptyOptionalToolArgs, normalizeOutputIndex, normalizeUpstreamFailure, extractResponsesReasoningSummaryText) verbatim into the pure leaf openai-responses/pureHelpers.ts (no stream state, no host import). Host imports them back and re-exports normalizeUpstreamFailure for external importers (tests). Host 1091 -> 1001 LOC. The stateful streaming core stays in the host (out of scope). Byte-identical bodies (multiset 73/73), no cycle. Adds a split-guard; consumer tests stay green (responses-translation-fixes 37, combo-param-validation-fallback-4519 5). * docs(compression): document upstream sync policy for RTK/Caveman engines (#5830) (#5948) Integrated into release/v3.8.44 — docs-only upstream sync policy for RTK/Caveman engines (closes #5830). All 7 checks green. * fix(sse): strip ANSI/VT100 codes from gemini-cli stream frames (#5934) Integrated into release/v3.8.44 — ReDoS-safe ANSI/VT100 strip for gemini-cli stream frames (port of upstream #2273, thanks @anki1kr). PR test green (5/5), file-size gate OK. * fix(translator): strict Anthropic content-block compliance in antigravity→openai request (#5935) Integrated into release/v3.8.44 — strict Anthropic content-block compliance in antigravity→openai (port upstream #2296). PR test green (9/9). UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path), not a regression from this PR. * fix(mcp): auto-recover stale streamable HTTP sessions on initialize (#5957) Integrated into release/v3.8.44 — MCP stale streamable-HTTP session auto-recovery (thanks @Chewji9875). * fix(providers): validate v0 Platform API keys via chats endpoint (#5954) Integrated into release/v3.8.44 — v0-vercel Platform API key validation (thanks @vittoroliveira-dev). * fix(api): relax provider-scoped chat completion validation (#5907) Integrated into release/v3.8.44 — relaxed provider-scoped chat validation + regression test (thanks @nickwizard). * fix(providers): strip /v1 unconditionally to avoid /v1/v1/models fetch error (#5899) (#5920) Integrated into release/v3.8.44 — unconditional /v1 strip in both models-discovery paths + regression test (thanks @anki1kr). * fix(resilience): per-window is_exhausted + honor quota-exhaustion preflight for priority combos (#5923) (#5941) Integrated into release/v3.8.44. * fix(resilience): honor active codex session affinity over per-request reset-aware re-scoring (#5903) (#5943) Integrated into release/v3.8.44. * fix(thinking): only inject redacted_thinking replay block when tool_use present and thinking enabled (#5945) (#5953) Integrated into release/v3.8.44. * feat(providers): add ClinePass API-key provider (#5942) Integrated into release/v3.8.44 — ClinePass API-key (BYOK) provider (port upstream 9router#2304, co-authored @adentdk). Validated locally: 16 clinepass tests green; fixed the APIKEY count 158→159 + translate-path golden snapshot (clinepass is a genuine new provider). Remaining UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path). Supersedes stub #5541. * feat(api): add /v1/ocr endpoint (Mistral OCR) + Mistral moderation (#5950) Integrated into release/v3.8.44 — /v1/ocr endpoint (Mistral OCR) + Mistral moderation (port upstream 9router#2064, co-authored @waguriagentic). Validated locally: 14 ocr-route tests + moderation/servicekind/endpoint-category suites green (CORS→Zod→handler + no-stack-leak assertion). Reds are inherited DRIFT only: cognitive-complexity ratchet (none from OCR files — pre-existing cycle drift, rebaselined at release) + environmental setup-claude base-red. * fix(codex): convert chat json schema to responses text format (#5933) Integrated into release/v3.8.44 — converts Chat Completions json_schema response_format → Responses API text.format on the Codex path, and preserves existing text.format through verbosity normalization. Base redirected main→release; the openai-responses.ts split that landed this cycle was reconciled by re-applying the delta onto openai-responses/toResponses.ts. Validated locally: 48 translator-openai-responses-req + 8 codex-verbosity tests green. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(providers): add Claude Sonnet 5 support across the model pipeline (#5833) Integrated into release/v3.8.44 — wires claude-sonnet-5 end-to-end (registries, modelSpecs, pricing ×3, cost, Sonnet-family fallback, 1M-ctx, static models). Reconciled the add/add overlap with the already-merged #5796 (kept the PR's superset test with the family-fallback assertion). Validated locally: kiro-sonnet-5 + catalog + pricing/modelSpecs/fallback suites all green. Thanks @ggiak! Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(relay): gate bifrost auto routing by provider manifest (#5870) Integrated into release/v3.8.44 — gates Bifrost auto-routing by the provider plugin manifest (only manifest-eligible providers reach the sidecar; ineligible/unknown fall back to the TS path with explicit reasons). Superset of #5869 (carries the full manifest + registry + docs). Resolved an integration-test conflict in favor of the release (which already subsumes this PR's readiness/removeDirWithRetry improvements). Validated locally: 4 provider-plugin-manifest + 11 relay-routing-backend tests green. Thanks @KooshaPari! Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * refactor(translator): extract pure message helpers from openai-to-kiro (852→751) (#5947) * refactor(translator): extract pure message helpers from openai-to-kiro Extract the pure tool/message helpers (parseToolInput, normalizeKiroToolSchema, serializeToolResultContent) verbatim into the leaf openai-to-kiro/messageHelpers.ts. The host imports them back for convertMessages. They were module-private, so the public export set is unchanged (no re-export needed). Host 852 -> 751 LOC. Byte-identical bodies (multiset 99/99), leaf has zero imports (no cycle). Adds a split-guard; consumer tests stay green (translator-openai-to-kiro 33, translator-ai-sdk-image-parts 3). * chore: re-trigger CI (stuck runner on 2/2 shard) * refactor(executors): extract pure prompt + composer helpers from cursor (#5960) Extract two pure clusters from the cursor executor into sibling leaves: - cursor/prompt.ts: isRecordLike + toolChoiceDirectiveLine + buildCursorOutputConstraints - cursor/composer.ts: composer thinking-as-content decoding (isComposerModel, visibleComposerContentFromThinking, composerReasoningRemainder + markers) Host imports both back for internal use and re-exports the 3 composer helpers for external importers (tests). Host 1576 -> 1451 LOC. Byte-identical bodies (verbatim multiset prompt 65/65, composer 32/32), leaves have zero imports (no cycle). Adds a split-guard; consumer tests stay green (cursor-composer-thinking, cursor-streaming, cursor-agent-tool-calls, translator-openai-to-cursor, cursor-agent-system-prompt). * refactor(executors): extract pure SSE-collect parsing from antigravity (#5962) Extract the pure SSE-payload -> collected-stream parser (AntigravityCollectedStream, stripZeroWidth, parseAntigravityTextualToolCall, addAntigravityTextualToolCall, processAntigravitySSEPayload/Text, flushAntigravitySSEText) verbatim into the leaf antigravity/sseCollect.ts. Host imports the helpers it uses and re-exports processAntigravitySSEPayload for external importers (tests). Host 1812 -> 1671 LOC. Byte-identical bodies (verbatim multiset 135/135), leaf does not import the host (no cycle). Credit/quota state, auth, and HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (executor-agy 8, executor-antigravity 26, antigravity-sse-collect-socket-release, copilot-agent-antigravity-parity 6). * refactor(executors): extract pure model maps + resolvers from chatgpt-web (#5967) Extract the static model maps (MODEL_MAP, MODEL_FORCED_EFFORT, THINKING_CAPABLE_SLUGS) and the pure thinking-effort resolvers (isThinkingCapableModel, normalizeThinkingEffort, resolveThinkingEffort, ResolvedChatGptModel, resolveChatGptModel) verbatim into the pure leaf chatgpt-web/models.ts. Host imports the two resolvers it uses back. Host 3205 -> 3076 LOC. Byte-identical bodies (verbatim multiset 120/120), leaf has zero imports (no cycle). Auth/PoW/session/HTTP dispatch and all module caches untouched. Adds a split-guard; consumer tests stay green (chatgpt-web 86, chatgpt-web-tools-5240 4, chatgpt-web-sha3-boringssl-5531 5). * refactor(executors): decompose grok-web into pure tool/markup leaves (#5994) Extract the pure OpenAI<->Grok tool-translation, native-tool mapping, markup cleanup, and NDJSON stream types out of the 1872-line grok-web executor into 4 sibling leaves: - grok-web/types.ts: GrokStreamResponse/GrokStreamEvent (stream types) - grok-web/tool-bridge.ts: OpenAI<->Grok tool translation + registry + classifiers - grok-web/native-tools.ts: native-tool selection/scoring + native->OpenAI mapping - grok-web/text-cleanup.ts: Grok markup stripping + GrokMarkupFilter Layered, acyclic: types <- tool-bridge <- native-tools; text-cleanup <- types; host imports the leaves. All symbols module-private (no host re-export). Host 1872 -> 887 LOC. Byte-identical bodies (verbatim per-leaf), no cycle, all new leaves <= 800 cap (tool-bridge split at line 753 to stay under). Auth/cookie/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (grok-web 62, grok-cli-oauth 15, grok-cli-strip-params 2). * refactor(executors): extract pure quota parsing from codex (#5999) Extract the pure Codex quota-snapshot parsing + reset/cooldown scheduling (CodexQuotaSnapshot, parseCodexQuotaHeaders, getCodexResetTime, getCodexDualWindowCooldownMs) verbatim into the leaf codex/quota.ts. Host re-exports the 4 symbols so handlers/chatCore/codexQuota.ts + tests keep resolving. Host 1539 -> 1427 LOC. Byte-identical bodies (verbatim 98/98), leaf has zero imports (only Date, no cycle). WS transport, auth, HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (executor-codex 40, codex-quota-fetcher 7, chatcore-codex-quota 5). * refactor(executors): extract pure stream formatters from deepseek-web (#6000) Extract the pure content/citation formatters (isThinkingModel, isSearchModel, cleanDeepSeekToken, formatStreamContent, DeepSeekSearchResult, appendSearchCitations) verbatim into the leaf deepseek-web/stream-format.ts. Host imports the 5 it uses back into transformSSE/collectSSEContent (cleanDeepSeekToken stays internal to the leaf). Host 1147 -> 1108 LOC. Byte-identical bodies (verbatim 34/34), leaf has zero imports (no cycle), all module-private (no re-export). PoW/auth/token-cache/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (deepseek-web 35, deepseek-web-rolling-window-2942 5, deepseek-web-tools-execute 3). * refactor(api): add validatedJsonBody helper (salvage #5075) (#5931) Fuses JSON body parsing + Zod validation into a single call that returns either type-narrowed data or a ready-to-return 400 NextResponse with the standard error envelope. Salvaged as the Tier 1 portable helper from the closed refactor PR #5075; the bulk route migration is intentionally not ported. Adds a focused 6-case regression test. Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com> * feat(qoder): drive PAT auth via qodercli, add dashboard quota, fix connection display (#5816) Integrated into release/v3.8.44 — Qoder PAT auth via qodercli binary + dashboard quota + dual-auth connection fix. Thanks @AgentKiller45 (co-author @judy459)! Validated locally (release-green on its own merits): lint 0, typecheck:core 0, 104 qoder/usage/UI tests green, file-size gate OK (owner-approved qoderCli.ts baseline-freeze 666→989), env-doc-sync fixed (documented QODER_CLI_CONFIG_DIR). The 2 remaining CI reds are INHERITED base-reds, not caused by this PR: (1) LEDGER-4 minimax-m3 supportsVision (minimax-m3 base + cline-pass/minimax-m3 from the already-merged #5942); (2) mutation-test-coverage missing 3 tests in stryker.conf (#5903/#5942/#5923). Both cleaned up separately. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(providers): minimax-m3 supportsVision (LEDGER-4) + stryker tap.testFiles drift (#6012) Release-green cleanup — clears LEDGER-4 minimax-m3 supportsVision + stryker tap.testFiles drift base-reds. Validated locally. * fix(registry): flag cline-pass/minimax-m3 as multimodal (supportsVision) (#6003) The cline-pass provider's minimax-m3 entry was missing supportsVision, breaking the LEDGER-4 registry-consistency test (all minimax-m3 entries must set supportsVision to match lite.ts — minimax-m3 is multimodal). Every other minimax-m3 registry entry (trae, bazaarlink, cline, ollama-cloud, ...) already sets it. This was a base-red on release/v3.8.44 inherited by every open PR. Validated by the existing failing-then-passing guard tests/unit/review-reviews-v3814-fixes.test.ts (LEDGER-4). * refactor(executors): extract pure payload construction from claude-web (#6006) Extract the pure Claude-web payload types + transforms + default tools/style (ClaudeWebRequestPayload, ClaudeWebStreamChunk, DEFAULT_CLAUDE_MODEL, generateMessageUUIDs, getDefaultTools, getDefaultPersonalizedStyle, transformToClaude, transformFromClaude) verbatim into the leaf claude-web/payload.ts. Host imports the 3 it uses back (ClaudeWebRequestPayload type + the two transforms). Host 1056 -> 835 LOC. Byte-identical bodies (verbatim 149/149), leaf imports only randomUUID (no host import, no cycle), all module-private (no re-export). Cookie/auth/ Turnstile/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (claude-web 13, claude-web-auto-refresh 6). * refactor(executors): extract pure upstream-header helpers from base (#6008) Extract the pure upstream-header helpers (mergeUpstreamExtraHeaders, getCustomUserAgent, setUserAgentHeader, applyConfiguredUserAgent, isOpenAICompatibleEndpoint, stripStainlessHeadersForOpenAICompat) verbatim into the leaf base/headers.ts. base.ts is imported by ~18 executors, so the host re-exports all 6 to keep those import paths intact; it also imports the 4 it uses internally in the BaseExecutor class. The trivial JsonRecord type alias is redefined locally in the leaf to avoid a base<->leaf cycle. Host 1539 -> 1451 LOC. Byte-identical bodies (verbatim 78/78), leaf does not import the host (no cycle). typecheck:core validates all base importers still resolve via the re-export. Adds a split-guard; consumer tests stay green (executor-base-utils 22, executor-default-base 49, executor-strip-stainless-openai-compat 6, plus executor sanity via typecheck). * refactor(executors): extract pure wire protocol from perplexity-web (#6014) Extract the pure Perplexity wire protocol (consts, SSE stream types, SSE parsing, OpenAI<->Perplexity message translation, request/query builders, content extraction, sseChunk) verbatim into the leaf perplexity-web/protocol.ts. Host imports back the 10 symbols it uses; everything module-private (no re-export). Session cache, TLS fetch, auth, and the executor class stay in the host. Host 1028 -> 534 LOC. Byte-identical bodies (verbatim), leaf imports only randomUUID (no host import, no cycle). Adds a split-guard; consumer tests stay green (perplexity-web 26, streaming-tools-5927 2, tls-client 6, key-validation-models 2). * refactor(executors): extract pure URL normalizers from default (#6015) Extract the pure per-provider chat-URL normalizers (normalizeBailianMessagesUrl, normalizeDataRobotChatUrl, normalizeAzureAiChatUrl, normalizeWatsonxChatUrl, normalizeOciChatUrl, normalizeSapChatUrl, normalizeXiaomiMimoChatUrl, normalizeOpenAIChatUrl, getOpenRouterConnectionPreset) verbatim into the leaf default/urlNormalizers.ts. Host imports them back into buildUrl/transformRequest; the now-dead build*ChatUrl/normalizeBaseUrl imports move to the leaf. All module-private (no re-export). Host 864 -> 815 LOC (shrunk below its frozen baseline). Byte-identical bodies (verbatim 45/45), leaf does not import the host (no cycle). buildHeaders/execute/auth untouched. Adds a split-guard; consumer tests stay green (executor-default-base 49, anthropic-compatible-bearer 3, strip-client-metadata 3). * feat(webfetch): support self-hosted FireCrawl instances (#5793) Integrated into release/v3.8.44 — self-hosted FireCrawl support (FIRECRAWL_BASE_URL/FIRECRAWL_TIMEOUT_MS). Re-cut clean onto the release tip (branch was fossilized from a pre-v3.8.40 snapshot). Validated: 4 firecrawl tests green, env-doc-sync + docs-sync pass. UNSTABLE red is the inherited environmental setup-claude base-red. * feat(xai): register XaiExecutor with reasoning-effort suffix parsing (#5800) Integrated into release/v3.8.44 — XaiExecutor with reasoning-effort suffix parsing. Re-cut clean onto the release tip (branch was fossilized). Validated: 6 xai-executor tests green, provider-consistency OK, typecheck:core 0 errors, env-doc-sync in sync. UNSTABLE red is the inherited environmental setup-claude base-red. * feat(discovery): Phase 2 — reporter, /api/discovery/* routes (strict loopback-only) + dashboard UI (#5939) * feat(discovery): Phase 2 reporter — discoveryResults DB module + service wiring Adds src/lib/db/discoveryResults.ts (CRUD over the discovery_results table from migration 074) and wires the opt-in discovery service to persist and read findings through it: persistDiscoveryResult / getDiscoveryResults / getDiscoveryResultById / markVerified / deleteDiscoveryResult, with (provider, method, endpoint) upsert de-duplication. Re-exported from localDb. The service stays opt-in / default-off. The /api/discovery/* routes and the dashboard UI tab are intentionally deferred to Phase 2b — they need the local-only enforcement model (Hard Rules #15/#17 territory) decided first. TDD: tests/unit/db/discovery-results.test.ts (8 cases, DB + service delegation), isolated DATA_DIR with resetDbInstance cleanup. * feat(discovery): Phase 2b — /api/discovery/* routes (strict loopback-only) Adds the discovery HTTP surface on top of the reporter DB module: GET /api/discovery/results list findings (optional ?providerId) GET /api/discovery/results/:id one finding (404 if absent) DELETE /api/discovery/results/:id delete a finding POST /api/discovery/scan scan a provider + persist findings POST /api/discovery/verify/:id mark a finding verified Authorization: strict loopback-only. "/api/discovery/" is added to LOCAL_ONLY_API_PREFIXES so the central authz pipeline (proxy.ts → runAuthzPipeline → managementPolicy) rejects non-loopback callers with a 403 LOCAL_ONLY before any handler runs. It is deliberately NOT in LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES — no remote manage-scope bypass — because POST /scan issues outbound probes to provider endpoints (SSRF-adjacent) and must never be tunnel-reachable. Handlers also call requireManagementAuth (defense in depth) and return sanitized errors via createErrorResponse. Tests: - tests/unit/authz/discovery-routes-local-only.test.ts (8) — security guard: isLocalOnlyPath true + not manage-scope-bypassable for all four paths. - tests/unit/api/discovery-routes.test.ts (6) — handler integration over an isolated DATA_DIR: list/filter, by-id 200/404/400, scan persist + 400 on empty/malformed body, verify 200/404, delete 200/404, no stack-trace leak. * feat(discovery): Phase 2c — dashboard UI tab (Tools → Discovery) Adds the /dashboard/discovery page (DiscoveryPageClient) that consumes the Phase 2b /api/discovery/* routes: scan a provider, list findings, verify or delete them. Registered in the sidebar under the Tools group (icon travel_explore) and given a "discovery" i18n namespace + sidebar keys in en.json (other locales fall back to en via next-intl until synced — the locale files are in a pre-existing coverage deficit unrelated to this change). Registers the UI test path in vitest.config.ts (advisory ui suite). Tests: src/app/(dashboard)/dashboard/discovery/__tests__/DiscoveryPageClient.test.tsx (3 cases: loads+renders results, empty state, fetches /api/discovery/results on mount; stable useTranslations mock to avoid the fetch-loop). NOTE: the ui vitest suite cannot run in this workspace — @testing-library/dom (a @testing-library/ react peer dep) is absent from node_modules, which fails ALL existing ui tests equally; the test runs in CI. Component verified locally via typecheck + lint. * test(discovery): register discovery-routes-local-only in stryker tap.testFiles The mutation-test-coverage gate (--strict) flags any unit test covering a mutated module that isn't listed in stryker.conf.json tap.testFiles. This PR's tests/unit/authz/discovery-routes-local-only.test.ts covers src/server/authz/ routeGuard.ts (a mutated module, which this PR edits by adding the /api/discovery/ local-only prefix), so it must be registered for its mutant kills to count. No behavior change. * refactor(discovery): split DiscoveryPageClient to satisfy max-lines-per-function The complexity ratchet (max-lines-per-function: 80) flagged the single 184-line DiscoveryPageClient function (+1 over baseline). Extract the data layer into two hooks (useDiscoveryResults for list/loading/feedback, useDiscoveryActions for scan/verify/delete), a shared callApi helper, and two presentational sub-components (DiscoveryScanForm, DiscoveryResultCard). Every function is now under the 80-line ceiling; complexity gate back to baseline 1995. No behavior change — same exported component, same endpoints, same props. * test(sidebar): include discovery in omni-proxy item-order snapshot Adding the Discovery item to the Tools group (this PR's sidebar entry) extends the ordered omni-proxy section list. Update the exact-match deepEqual snapshot in sidebar-visibility.test.ts to include "discovery" in its position (after traffic-inspector). The assertion stays exact — this reflects the intentional new item, it does not weaken the check. * docs(changelog): restore release bullets eaten by merge auto-resolve; re-add discovery bullet additively * chore(quality): bump testFrozen for translator-openai-responses-req.test.ts (1097 -> 1172) Base-red inherited from #5933, which grew the test file to 1171 lines (Hard Rule #18 regression tests) without adjusting the frozen cap. The release tip itself fails check:file-size; this unblocks every PR into release/v3.8.44. File untouched by this PR. * chore(quality): restore stryker tap.testFiles entries eaten by merge auto-resolve The merge of origin/release/v3.8.44 silently dropped the 3 entries added on the release side (#5903, clinepass, #5923). Took the release version verbatim and re-added only this PR's entry (discovery-routes-local-only) in alphabetical order. check:mutation-test-coverage green locally. * chore(quality): reconcile inherited v3.8.44 merge-burst drift + include discovery in tools-group order test - complexity 1995->2003 and cognitive 856->859: both measure IDENTICAL on the pristine release tip ( |
||
|
|
604afeacf4 |
Revert "fix(config): externalize ws for copilot-m365-web executor (#6098, closes #6062)"
This reverts commit
|
||
|
|
e61b75f007 |
fix(config): externalize ws for copilot-m365-web executor (#6098, closes #6062)
Externalize ws / bufferutil / utf-8-validate in serverExternalPackages so the copilot-m365-web WebSocket masking path works at runtime (bundling ws → TypeError: b.mask is not a function → 80s chat timeout). Regression guard in next-config.test.ts. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |