mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
feat/plugin-browser-pool
5622 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
311fd35d8b | docs(readme): Kimi partner tracking links (aff=omniroute) + first-Brazilian-project line + disclosure (#8028) | ||
|
|
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> |
||
|
|
eab59d4048 |
fix(translators): normalize TitleCase tool names for non-Anthropic models (#7926)
* fix(translators): normalize TitleCase tool names for non-Anthropic models * fix(translator): parse XML <invoke> blocks in OpenAI→Claude response translator * fix(gemini-web): mark gemini-3.1-flash-lite as toolCalling: false * fix(gemini-web): mark all models as toolCalling: false, add to no-tools combo * fix(web-providers): mark all web-cookie models as toolCalling: false * fix(nvidia): disable tool calling on models that can't handle it * fix(lint): drop explicit any annotation on extractXmlInvokeBlocks state param Removes the no-explicit-any violation introduced alongside the TitleCase tool-name normalization work so the merged branch stays lint-clean (state stays implicitly typed like its sibling helpers in this file). 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> |
||
|
|
55549bfe5a |
feat(sse): add PromptQL playground provider (unofficial) (#7911)
* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (#7168) * fix(ci): add the auto-enqueue pull_request_rule to the Mergify config (queue_conditions alone are eligibility-only) (#7179) * fix(ci): migrate Mergify auto-enqueue to merge_protections_settings.auto_merge_conditions (rules-based path is EOL 2026-07-16) (#7216) * fix(ci): drop Mergify batch settings (batching is a paid-tier feature; free plan queue is serial) (#7220) * fix(ci): merge queue tolerates the advisory dast-smoke failure (its GH-hosted build hang dequeued every attempt) (#7225) * test(ci): make the #6634 selfref guard hermetic — main's copy hard-fails every PR (#7341) main's copy of this test still does git I/O inside a unit test: const baseSrc = git(['show', 'origin/main:' + FILE]); Runners check out a shallow single ref, so origin/main does not resolve and the test dies with 'fatal: invalid object name origin/main'. Every PR into main fails Unit Tests (7/8) on it — today that is #7313, #7315, #7316, #7334, #7336 and #7337, six PRs red on a defect none of them introduced. #7313 has no other red at all. release/v3.8.49 already carries a fix ( |
||
|
|
19cbe8ae14 |
fix(providers): route iflytek/sparkdesk to Spark's OpenAI-compatible host (#7942)
Both entries declare format: "openai" with authHeader: "bearer", but pointed at spark-api.xf-yun.com — Spark's WebSocket host, which authenticates with an HMAC-SHA256 signature over app_id/apiKey/apiSecret and rejects bearer tokens. The OpenAI-compatible HTTP API lives on spark-api-open.xf-yun.com/v1, so neither provider could complete a request as configured. sparkdesk also listed a "general" model; that is a WebSocket domain value and is not accepted by the HTTP endpoint, so it becomes "lite" (Spark Lite). The free-model catalog's sparkdesk row is updated to match, and a regression test locks both baseUrls, the removed "general" model id, and catalog/registry cross-reference. Reconstructed against release/v3.8.49 (folds in the same-PR follow-up "point sparkdesk free-catalog row at lite"). Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
a865fddb26 |
fix(perplexity-web): multi-step empty content + advanced-quota cooldown (#7930)
Perplexity's live multi-step/copilot streams can surface the advanced_models_quota_low upsell instead of any answer text when the account's weekly advanced-model budget is exhausted. Detect it and return HTTP 429 with reset_seconds/Retry-After (mapped to rate_limited_until) instead of a silent empty-content error. Also fixes plan-goal (thinking) extraction for live multi-step streams that deliver the plan as an RFC-6902 diff patch against plan_block instead of a materialized plan_block object — those goals were previously dropped. Reconstructed against release/v3.8.49: most of the original "empty content" fix in this PR was independently and differently addressed on release already (extractAnswerFromFinalText + longestMarkdownAnswer), so only the two non-overlapping pieces above are ported here. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
f31f3c081e |
feat(proxy): operator-level proxy subscriptions (Karing-style) — hardened, ready for review (#7299)
* feat(proxy-subscriptions): src/lib/proxySubscription/parse.ts * feat(proxy-subscriptions): src/lib/proxySubscription/subscriptionService.ts * feat(proxy-subscriptions): src/lib/proxySubscription/index.ts * feat(proxy-subscriptions): src/lib/db/migrations/123_proxy_subscriptions.sql * feat(proxy-subscriptions): src/app/api/v1/management/proxy-subscriptions/route.ts * feat(proxy-subscriptions): src/app/api/v1/management/proxy-subscriptions/[id]/route.ts * feat(proxy-subscriptions): src/app/api/v1/management/proxy-subscriptions/[id]/refresh/route.ts * feat(proxy-subscriptions): src/app/api/v1/management/proxy-subscriptions/[id]/nodes/route.ts * feat(proxy-subscriptions): src/app/(dashboard)/dashboard/settings/components/proxy/SubscriptionTab.tsx * feat(proxy-subscriptions): tests/unit/proxySubscription.parse.test.ts * feat(proxy-subscriptions): tests/unit/proxySubscription.service.test.ts * feat(proxy-subscriptions): docs/proxy-subscriptions.md * feat(proxy-subscriptions): src/lib/db/proxies/types.ts * feat(proxy-subscriptions): src/lib/db/proxies/mappers.ts * feat(proxy-subscriptions): src/lib/db/proxies.ts * feat(proxy-subscriptions): src/app/(dashboard)/dashboard/settings/components/ProxyTab.tsx * i18n(proxy-subscriptions): add proxySubscriptionsTab key + use in ProxyTab * i18n(proxy-subscriptions): add proxySubscriptionsTab key + use in ProxyTab * i18n(proxy-subscriptions): add proxySubscriptionsTab key + use in ProxyTab * i18n(proxy-subscriptions): add proxySubscriptionsTab key + use in ProxyTab * test(proxy-subscriptions): extract isSubscriptionDue + unit tests * test(proxy-subscriptions): extract isSubscriptionDue + unit tests * test(proxy-subscriptions): extract isSubscriptionDue + unit tests * test(proxy-subscriptions): add global->rule switch re-bind integration test * feat(proxy-subscriptions): inline needs-local-core guidance in SubscriptionTab * i18n: add proxySubscriptionsTab to en (rebased on current main) * i18n: add proxySubscriptionsTab to zh-CN (rebased on current main) * i18n: add proxySubscriptionsTab to pt-BR (rebased on current main) * test(proxy-subscriptions): extract needsCore detection into pure module + unit tests * test(proxy-subscriptions): extract needsCore detection into pure module + unit tests * test(proxy-subscriptions): extract needsCore detection into pure module + unit tests * refactor(proxy-subscriptions): extract scopes.ts into pure module + unit tests (#65) * refactor(proxy-subscriptions): extract coreEndpoint.ts into pure module + unit tests (#65) * refactor(proxy-subscriptions): extract subscriptionService.ts into pure module + unit tests (#65) * refactor(proxy-subscriptions): extract proxySubscription.scopes.test.ts into pure module + unit tests (#65) * refactor(proxy-subscriptions): extract proxySubscription.coreEndpoint.test.ts into pure module + unit tests (#65) * security(proxy-subscriptions): fetchGuard.ts — SSRF guard + core scheme (#P0) * security(proxy-subscriptions): coreEndpoint.ts — SSRF guard + core scheme (#P0) * security(proxy-subscriptions): subscriptionService.ts — SSRF guard + core scheme (#P0) * security(proxy-subscriptions): proxySubscription.fetchGuard.test.ts — SSRF guard + core scheme (#P0) * security(proxy-subscriptions): proxySubscription.coreEndpoint.test.ts — SSRF guard + core scheme (#P0) * refactor(proxy-subscriptions): subscriptionService.ts — concurrency lock / resilience / url redaction (#P1) * refactor(proxy-subscriptions): url.ts — concurrency lock / resilience / url redaction (#P1) * refactor(proxy-subscriptions): index.ts — concurrency lock / resilience / url redaction (#P1) * refactor(proxy-subscriptions): route.ts — concurrency lock / resilience / url redaction (#P1) * refactor(proxy-subscriptions): route.ts — concurrency lock / resilience / url redaction (#P1) * refactor(proxy-subscriptions): proxySubscription.url.test.ts — concurrency lock / resilience / url redaction (#P1) * i18n(proxy-subscriptions): subscriptionService.ts — stable error codes + locale keys (#P2-6) * i18n(proxy-subscriptions): SubscriptionTab.tsx — stable error codes + locale keys (#P2-6) * i18n(proxy-subscriptions): en.json — stable error codes + locale keys (#P2-6) * i18n(proxy-subscriptions): zh-CN.json — stable error codes + locale keys (#P2-6) * i18n(proxy-subscriptions): pt-BR.json — stable error codes + locale keys (#P2-6) * i18n(proxy-subscriptions): en.json — normalize to LF line endings (#P2-6) * i18n(proxy-subscriptions): zh-CN.json — normalize to LF line endings (#P2-6) * i18n(proxy-subscriptions): pt-BR.json — normalize to LF line endings (#P2-6) * enhance(proxy-subscriptions): reject subscription fetch if ANY resolved DNS address is blocked (P3-1) * enhance(proxy-subscriptions): add withRetry() exponential-backoff helper (P3-2) * enhance(proxy-subscriptions): DNS multi-record guard + retry/backoff fetch + batch scope writes + observability (P3-1..P3-4) * enhance(proxy-subscriptions): batch addProxiesToScopePool() to drop N+1 writes (P3-3) * enhance(proxy-subscriptions): add last_error_at + consecutive_failures observability columns (P3-4) * enhance(proxy-subscriptions): surface consecutive failures + last error time in subscription cards (P3-4) * test(proxy-subscriptions): cover multi-record DNS SSRF (block if ANY address internal) (P3-1) * test(proxy-subscriptions): cover withRetry() first-success / retries / backoff / non-retryable stop (P3-2) * fix(proxy-subscriptions): resolve js-yaml import + missing backup/generation-bump imports Two bugs made the feature non-functional and its own test suite false: 1. parse.ts used `import yaml from "js-yaml"` (default import), but js-yaml@^5 is ESM-only with no default export — this threw a SyntaxError at module load, crashing every caller (index.ts re-exports parse.ts, so every API route hit this too). Switch to `import * as yaml`, matching how the rest of the codebase already imports js-yaml (hermes-agent.ts, openapiParser.ts, openapi/spec route.ts, guide-settings route.ts). 2. subscriptionService.ts's unapplySubscription() called backupDbFile() and bumpProxyRegistryGeneration() without importing either — backupDbFile exists but wasn't imported; bumpProxyRegistryGeneration was a private, non-exported function in db/proxies.ts. This threw a ReferenceError whenever a subscription with bound proxies was disabled/deleted (the normal path). Import backupDbFile from ../db/backup and export+import bumpProxyRegistryGeneration from ../db/proxies, matching the identical backup+bump pattern already used by deleteProxyById for the same proxy_assignments/proxy_registry mutation shape. Fixing both unmasked a third, previously-unreachable bug (both crashes happened before any test assertion could run): recomputeProxyEnabled() checked `proxy_subscriptions.enabled = 1` alone, which stays true across an unapply/disable cycle since unapplySubscription() never touches that column — the proxyEnabled flag would get stuck on `true` even after the subscription's proxies were fully detached. Changed the check to require an actually-bound proxy_assignments row for an enabled subscription, matching hasNonSubscriptionGlobalProxy()'s existing bound-check pattern. Traced all 3 production call sites (mode/rule switch, disable, delete) to confirm this doesn't change their outcome — only the previously-wrong "unapply in isolation" case. Also fixed the test file's own pre-existing bug: its provider_connections inserts omitted created_at/updated_at (NOT NULL, no default in the schema since 001_initial_schema.sql), which 0 assertions had ever reached before because the SyntaxError always crashed the file first. All 9 proxySubscription test files now run clean: 49/49 pass (previously 2 files crashed outright at import time, 0 assertions ever ran). Separately confirmed via testing against the pristine PR head: this PR has 3 more pre-existing gate failures unrelated to the above (file-size on db/proxies.ts, cognitive-complexity, complexity, changelog-integrity vs the current release tip) plus 3 pre-existing TS2345 errors in parse.ts (lines 228/233/263, unrelated to the yaml import). All are the PR's own scope/base-drift, out of scope for this fix — the PR has never had a real CI run (base=main), so no gate has ever surfaced them; they need the base retarget + a full CI pass called out in the plan file's own remaining mandatory items. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(db): renumber proxy-subscriptions migrations to avoid version collision release/v3.8.49 already ships 123_quota_auto_ping.sql and 124_generic_session_affinity_ttl.sql; this PR's 123/124 files collided, tripping migrationRunner's version-collision guard and failing all proxySubscription.service tests. Renumber to 127/128 (next free slots after 126_reasoning_routing_rules.sql). Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@users.noreply.github.com> * refactor(db): extract proxySubscriptions + registryGeneration to keep proxies.ts under file-size cap Moves addProxiesToScopePool to ./proxySubscriptions.ts and the registry-generation helpers to ./proxies/registryGeneration.ts (re-exported from proxies.ts), keeping the module under its frozen 1177-line cap after the operator-proxy-subscriptions feature. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(db): renumber proxy-subscriptions migrations past release collision Rebasing onto release/v3.8.49 surfaced a migration version collision: this branch's 127_proxy_subscriptions.sql and 128_proxy_subscriptions_meta.sql now collide with 127_usage_history_account_identity.sql and 128_auto_candidate_overrides.sql that landed on release since this branch last synced. Renumbered to 131/132 (next free prefixes after the current 130_remove_unregistered_qwen_data.sql) and updated the in-file header comments to match. No schema/behavior change. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- 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: Diego Rodrigues de Sa e Souza <diegosouzapw@users.noreply.github.com> Co-authored-by: xier2012 <xier2012@users.noreply.github.com> |
||
|
|
124557cc4b |
fix(combo): context-aware fallback ignores model_context_override (#7933)
* fix(combo): context-aware fallback ignores model_context_override filterTargetsByRequestCompatibility resolved a target's context capacity through the override-free getResolvedModelCapabilities, so a persisted model_context_override (Feature 5004) never reached the compatibility filter. A provider whose catalog maxInputTokens is a deliberately small client-facing hint (below the real window so coding agents auto-compact, #6191) is then dropped from the fallback pool for large-context requests even when an operator recorded its true larger capacity. With only that provider left after Claude quota is exhausted, the combo returns a hard 503 with no fallback. evaluateContextLimit now consults the raw override (getModelContextOverride, null when unset) before catalog limits, only when an override exists - so a genuinely-too-small maxInputTokens is still enforced for non-overridden models. Consistent with two other override-aware call sites in this file. Registry values (#6191 client hint) untouched. Tests: override rescues small-catalog target; without override too-small still dropped; existing #6191/#7039 tests pass (14/14). * fix(quality): extract context-fit evaluation to keep comboStructure.ts under cap The model_context_override fix grew open-sse/services/combo/comboStructure.ts past the 800-line file-size cap. Extract evaluateContextLimit() (the override-then-catalog context-fit check) into a new leaf, open-sse/services/combo/contextOverrideGate.ts, so comboStructure.ts only keeps the two call-site wires. No behavior change. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: tmone <25759142+tmone@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
2d1801985c |
feat(cline): align ClinePass catalog and request protocol (#7914)
* feat(cline): align catalogs and official protocol * fix(models): clean imports after final connection removal * fix(quality): extract Cline/ClinePass auth-header wiring to shrink default.ts open-sse/executors/default.ts grew to 894 lines against the frozen 890-line cap after adding the ClinePass official-protocol import plus two Object.assign header-merge blocks. Extract the merge logic into a new applyClineAuthHeaders() helper in src/shared/utils/clineAuth.ts so the executor's case "clinepass" / case "cline" branches shrink to a single call each, dropping default.ts back to 875 lines. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
a20771d6ac |
fix(auto): pool accounts by provider model (#7928)
* fix(auto): pool accounts by provider model * test(auto): update provider-family-combos to the #7928 Cartesian pool shape Since #7928 the auto-combo candidate pool is a connections × models Cartesian product, so createBuiltinAutoCombo("auto/<family>") now surfaces each backend's full family line-up rather than exactly one default model per connection. The #6453 invariant is unchanged and still asserted — which providers span the family and that unrelated providers (the connected openai/gpt-4o-mini, the connected glm on auto/zai) are excluded — but the two exact-count assertions are relaxed to the provider SET and the detectModelFamily() family check, matching the pattern the sibling auto/minimax case already uses. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Your Name <you@example.com> Co-authored-by: adrianaryaputra <adrian.arya@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
11b5ce1f0e |
feat(providers): add 5 free-tier providers (ainative, aion, sealion, routeway, nara) (#7887)
Five OpenAI-compatible free-tier aggregators OmniRoute did not cover yet, added
as a registry entry plus a canonical provider each.
ainative api.ainative.studio/api/v1 — 84-model public catalog, passthrough
aion api.aionlabs.ai/v1 — 5 models, public catalog w/ pricing
sealion api.sea-lion.ai/v1 — AI Singapore, pinned models (10 RPM)
routeway api.routeway.ai/v1 — 236-model catalog; browser UA pinned
because Cloudflare 1010s non-browser UAs
nara router.bynara.id/v1 — shared 5M/day pool, pinned free models
All five /models endpoints were probed live 2026-07-20 (200 for the public ones;
sealion/nara 401 without a key, as expected) and every pinned model id was
confirmed to exist upstream.
Free-catalog honesty: ainative ("~10M tok/mo claimed"), aion (20k tok/day),
sealion (10 RPM) and routeway (200 RPD) have no verifiable monthly TOKEN quota,
so they are recurring-uncapped — real access, never summed into the headline.
Only nara publishes a token figure (5M tokens/day shared = 150M/month), recorded
as one deduped pool. Net: 484 -> 514 models, 1.376B -> 1.526B tokens, the +150M
coming solely from nara.
|
||
|
|
f909b1d45e |
fix(resilience): don't cool down accounts or trip the breaker on client aborts (#7908)
* fix(resilience): don't cool down accounts or trip the breaker on client aborts When the caller drops the connection mid-stream, the in-flight request surfaces request_signal_aborted, "Client disconnected", or a DOM AbortError with no upstream status code. These shapes were counted as provider failures: the serving connection went into cooldown, the provider circuit breaker accrued failures, and healthy accounts ended up marked unavailable from client-side cancellations alone. Treat client aborts as local stream lifecycle events (#4602 policy): extend isLocalStreamLifecycleError() to recognize abort shapes and skip connection disable and breaker accounting for them. Genuine upstream failures (5xx/429/401) are still counted. Fixes #7907. * fix(resilience): guard the two remaining breaker-trip call sites against client aborts (#7907) PR #7908 correctly wired isLocalStreamLifecycleError() into shouldSkipConnDisable() and chatHelpers.ts's onStreamFailure, but two separate breaker._onFailure()-triggering call sites were purely status-code gated and never checked it, so a client-side abort (no upstream status, defaults to 502, error='request_signal_aborted') still tripped the whole-provider circuit breaker — the highest blast-radius of the 3 resilience mechanisms: - src/sse/handlers/chat.ts: the single-model, non-combo terminal-failure path called breaker._onFailure() directly, bypassing the isFailure option (which only applies inside breaker.execute()). Extracted the predicate into shouldTripProviderBreakerForResult() and added the missing isLocalStreamLifecycleError guard. - open-sse/services/combo/comboPredicates.ts::shouldRecordProviderBreakerFailure(), used by handleComboChat's executeTarget (open-sse/services/combo.ts), gained the same guard via a new optional `error` field. Added tests/unit/circuit-breaker-abort-provider-trip-7907.test.ts exercising both real predicates directly (not just the isolated isLocalStreamLifecycleError() helper) — confirmed red on the unfixed code (missing export) and green after the fix, alongside the existing #4602/#7908/combo-breaker-429 suites (24/24 pass, no regressions). file-size-baseline.json: +1 combo.ts (irreducible call-site wiring for the new `error` field) and +1 chatHelpers.ts (own growth from the PR's already-verified onStreamFailure guard, surfaced only now since fast-gates PR->release skip check:file-size). Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@users.noreply.github.com> Co-authored-by: insoln <insoln@ya.ru> * test(quality): register circuit-breaker abort tests in stryker tap.testFiles The two new tests (circuit-breaker-abort-provider-trip-7907, circuit-breaker-client-abort) import mutated modules (circuitBreaker.ts, comboPredicates.ts) but were not listed in stryker.conf.json tap.testFiles, tripping check:mutation-test-coverage --strict. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- 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> |
||
|
|
4e57c1dc9a |
fix(electron): derive macOS Helper name from execPath to remove 2nd Dock icon (#7941) (#8002)
resolveNodeExecutable() built the macOS Helper path from app.getName() (package.json
name = "omniroute-desktop"), but electron-builder names the Helper.app bundles from
build.productName ("OmniRoute"). The two diverged, so the probe never matched a real
Helper and fell through to process.execPath — spawning the main Electron binary via
ELECTRON_RUN_AS_NODE, which macOS renders as a second, inert Dock icon.
Extracted the resolution into electron/lib/resolveNodeHelper.js (pure, unit-testable),
deriving the Helper name from path.basename(process.execPath) so it always tracks the
productName-generated bundle. Registered the new module in electron build.files.
Reported by Carlos Espinoza (Carloss616) with runtime instrumentation of a packaged
.app pinpointing the exact field divergence.
|
||
|
|
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 (
|
||
|
|
246b87f739 |
fix(mitm): gate Agent Bridge DNS and Trust Cert on sudo password (#7938) (#7939)
* fix(mitm): gate Agent Bridge DNS and Trust Cert on sudo password (#7938) Extend the #7865 sudo gate to setup wizard DNS, Start DNS, and Trust Cert. Start server still runs without a password but skips privileged cert/DNS steps instead of spawning sudo -S with an empty string. Add a shared sudo password modal on the Agent Bridge page for DNS and trust-cert actions. Fixes #7938 * fix(mitm): use .tsx extension for MitmSudoPasswordModal hook JSX in a .ts file broke dashboard typecheck and ESLint on CI. * fix(mitm): skip DNS teardown on stop when sudo password missing (#7938) stopMitm() no longer invokes removeDNSEntry with an empty password when the server was started in skip mode. The MITM process is still killed. * refactor(mitm): extract privileged step helpers to satisfy file-size cap manager.ts exceeded the 800-line cap after #7938 gates. Move DNS teardown and the shared sudo skip runner into dedicated modules; behavior unchanged. |
||
|
|
567736fac5 |
fix(ccr): resolve principal via OMNIROUTE_API_KEY env var on stdio MCP transport (#7932)
CCR stores blocks keyed by principalId (the API key's DB row id). On
stdio transport there is no HTTP context, so resolveMcpCallerApiKeyId()
always returned undefined and the fallback resolved to 'anonymous' —
a store-key miss ('block not found').
Add resolvePrincipalFromEnv() that reads OMNIROUTE_API_KEY or
ROUTER_API_KEY from the environment and resolves through the same
getApiKeyMetadata() lookup that storage uses. Both storage and retrieval
now get the same principal id, so the store key matches.
Closes #7883
Co-authored-by: Erick Kinnee <erick@ekinnee.dev>
|
||
|
|
448257f8f7 |
fix(autostart): adopt 9Router VBS startup to suppress console flash on Windows (#7925)
Windows auto-start previously wrote a HKCU\Run registry entry that launched node.exe directly, causing a visible console window at logon. Closing that window also killed the background server. Adopt the same approach as 9Router: write a OmniRoute.vbs script to the Windows Startup folder that calls WScript.Shell.Run with SW_HIDE (0) so OmniRoute starts fully hidden. The VBS runs serve --no-open --tray via the existing buildServeExecLine helper. Legacy migration — enableWin() removes the old HKCU\Run entry so stale entries don't linger, and isEnabledWin() falls back to checking the registry for users who enabled autostart before this change. Co-authored-by: tientien17 <tientien17@users.noreply.github.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> |
||
|
|
7c39a0e065 |
chore(deps): bump js-yaml, brace-expansion, shell-quote, tar (security) (#7915)
Resolves all 11 open Dependabot alerts (lockfile-only; no runtime code change): - js-yaml 4.2.0 -> 4.3.0 (CVE-2026-59869 / GHSA-52cp-r559-cp3m, high) - root + electron - brace-expansion 1.1.x/2.1.1/5.0.6 -> 1.1.16/2.1.2/5.0.7 (CVE-2026-13149 / GHSA-3jxr-9vmj-r5cp, high) - root + electron - shell-quote 1.8.4 -> 1.10.0 (CVE-2026-13311 / GHSA-395f-4hp3-45gv, high) - root. concurrently (latest 10.0.3) pins shell-quote exactly at 1.8.4, so this adds a per-parent override, same pattern as @yarnpkg/parsers.js-yaml - tar 7.5.16 -> 7.5.20 (CVE-2026-59871 / GHSA-w8wr-v893-vjvp, medium) - root + electron |
||
|
|
62d46ba37c |
fix(api): resolve local provider models via dashboard catalog fallback (#7927)
The /api/v1/providers/{id}/models endpoint returns invalid_provider for
local self-hosted providers (Ollama, LM Studio, vLLM, etc.) because it
only resolves providers via getRegistryEntry() from the open-sse
registry, which does not include LOCAL_PROVIDERS.
After getRegistryEntry() misses, fall back to the dashboard-facing
provider catalog (getProviderById / getProviderByAlias from
@/shared/constants/providers), which covers LOCAL_PROVIDERS and all
other dashboard provider categories. Registry hits retain existing
precedence.
Closes #7910
Co-authored-by: Erick Kinnee <erick@ekinnee.dev>
|
||
|
|
43fac40b28 |
docs(guides): document Kaspersky PDM behavioral false positive on the Desktop installer (#7903) (#7923)
Adds a Kaspersky subsection to the existing 'Antivirus False Positives' troubleshooting section: explains PDM:Trojan.Win32.Generic is a behavioral heuristic on the unsigned installer, which files it rolls back (playwright / tls-client native DLL) and why, plus how to verify the sha512 against latest.yml, restore + exclude, and report the FP to Kaspersky. Refs #7903, #5946 |
||
|
|
3f2d88b679 |
fix(cli): spawn opencode.cmd shim with shell:true on win32 (#7913) (#7964)
* fix(cli): spawn opencode.cmd shim with shell:true on win32 (#7913) runOpenCodeAuth spawned the opencode.cmd shim with shell:false on win32, which throws spawnSync EINVAL under Node's hardened child_process handling (post CVE-2024-27980). Mirrors the fix already applied to codex (resolveCodexSpawn in launch-codex.mjs, #6263) and qodercli/Auggie (#6263/#6304): shell:isWin, unchanged elsewhere. Exported runOpenCodeAuth for testability and added a regression test covering both the win32 shell:true path and the non-regressing linux/darwin bare-binary path. * fix(cli): make opencode --auth win32 spawn testable without module mocks (#7913) The regression test used t.mock.module, which needs --experimental-test-module-mocks and fails to even run in CI (TypeError: t.mock.module is not a function → the fix was unvalidated). Extract a pure resolveOpenCodeAuthSpawn(providerId, platform) helper and test it directly (win32 → shell:true, linux/darwin → shell:false). Production behavior of runOpenCodeAuth is unchanged. |
||
|
|
4eea1cd14f |
fix(auth): restore configurable HEALTHCHECK_BATCH_SIZE dropped by #7719 (#7875) (#7970)
* fix(auth): restore configurable HEALTHCHECK_BATCH_SIZE dropped by #7719 (#7875) * docs(env): document HEALTHCHECK_BATCH_SIZE in .env.example (#7875) * docs(env): document HEALTHCHECK_BATCH_SIZE in .env.example + ENVIRONMENT.md (#7875) |
||
|
|
39ccfcf28c |
fix: parse Gemini 429 RetryInfo.retryDelay for model lockout (#7940) (#7961)
* fix(sse): parse Gemini 429 RetryInfo.retryDelay for model lockout (#7940) Gemini free-tier 429 bodies carry a short explicit retry hint -- error.details[].{"@type": google.rpc.RetryInfo, retryDelay: "26s"} plus a "Please retry in Ns." message -- but parseRetryFromErrorText only matched 'reset after'/'will reset after' text, so quotaResetHintMs came back null and recordModelLockoutFailure fell back to getMsUntilTomorrow() for quota_exhausted, locking the model out for ~19h instead of ~26s. parseRetryHintFromJsonBody (retryAfterJson.ts) now walks error.details[] for a google.rpc.RetryInfo entry and parses its retryDelay via a shared parseDelayString helper (moved out of accountFallback.ts so parseRetryAfterFromBody and the model-lockout path use the same grammar). parseRetryFromErrorText also gained a 'please retry in Ns' text fallback for bodies without a parseable details[] array. Both new paths are capped by a dedicated MAX_SHORT_RETRY_HINT_MS (24h), independent of the existing 30-day MAX_PROVIDER_COOLDOWN_MS, since RetryInfo/please-retry-in are short throttling hints, not long-lived quota resets like Antigravity's 160h. Regression test: tests/unit/bug-7940-gemini-retrydelay.test.ts (RED before the fix: parseRetryFromErrorText returned null and the resulting lockout was ~19h; GREEN after: ~26s). * chore(quality): register bug-7940-gemini-retrydelay test in stryker tap.testFiles |
||
|
|
0d4fbfeaec |
fix(sse): preserve parallel_tool_calls for GPT-5.6 delegation under Codex Responses Lite (#7821) (#7957)
* fix(sse): preserve parallel_tool_calls for GPT-5.6 ultra/max delegation under Codex Responses Lite (#7821) * fix(codex): drop over-broad parallel_tool_calls allowlist entry — keep #2608 stripping intact (#7821) The static RESPONSES_API_ALLOWLIST addition made parallel_tool_calls survive for ALL models, breaking the #2608 non-passthrough stripping guarantee for gpt-5.5. The real #7821 fix (isCodexDelegationDependentModel gating in enforceCodexResponsesLiteParallelToolCalls) is model/effort-scoped and does not need the allowlist entry — native Codex traffic returns before the allowlist runs. |
||
|
|
a47ce51d4c |
fix(api): classify /api/acp/agents as loopback-only (#7948) (#7966)
* fix(api): classify /api/acp/agents as loopback-only (#7948) /api/acp/agents is spawn-capable (POST registers a client-chosen `binary` via src/lib/acp/registry.ts; GET / POST {action:"refresh"} runs detectInstalledAgents() -> execFileSync(probe.command, probe.args, { shell }) transitively) but was never added to LOCAL_ONLY_API_PREFIXES in src/server/authz/routeGuard.ts, unlike every sibling spawn-capable route (Hard Rules #15/#17). A leaked JWT via tunnel could reach the version-probe execFileSync path. Adds the prefix to the loopback gate plus a direct regression test (tests/unit/route-guard-acp-agents-local-only.test.ts) and an assertion in tests/unit/check-route-guard-membership.test.ts documenting why the existing source-scan subcheck cannot catch this class of gap (the spawn call is transitive via registry.ts, not in the route file itself). * chore(quality): register route-guard-acp-agents-local-only test in stryker tap.testFiles |
||
|
|
68aa977593 |
fix(test): widen ratelimit-admission pollUntil deadline to 10s (#7842) (#7971)
The nightly-compat Node 24/26 shard failures were 17/18 base-red debt already fixed forward on the release tip (same pattern as #7025/#7140/#7675/#7742, tracked in #6949). The one genuine still-reproducing failure was a test-harness timing-margin bug: ratelimit-admission-control-6593's pollUntil helper used a fixed 2000ms deadline that races Bottleneck's QUEUED->EXECUTING event-loop-tick transition under CI/devbox contention. Widened the default deadline to 10000ms; the admission logic itself (checkQueueAdmission / RATE_LIMIT_QUEUE_FULL / 429) is unchanged and covered by 3 passing pure-unit tests. |
||
|
|
ce80af6c3d | fix(dashboard): correct block-extra-Claude-usage toggle copy to match quarantine behavior (#7918) (#7965) | ||
|
|
68a883f0c6 |
fix(cli): translate missing sqlite bindings error into actionable guidance (#7868) (#7963)
createSqliteNativeError() in bin/cli/sqlite.mjs only recognized the ABI-mismatch native error class (NODE_MODULE_VERSION/ERR_DLOPEN_FAILED). It silently passed through the 'Could not locate the bindings file' class thrown by the bindings package when the better-sqlite3 native addon was never built/downloaded - the exact case hit by 'npx omniroute reset-password', since npx runs a fresh ephemeral install that never builds the addon. Users got a raw multi-line path dump instead of guidance. Widen the condition to also match 'Could not locate the bindings file', MODULE_NOT_FOUND, and "Cannot find module 'better-sqlite3'", and point users at the existing self-heal command `omniroute runtime repair`, same as the ABI-mismatch branch already does. Regression test: tests/unit/cli-sqlite-bindings-not-found-7868.test.ts - RED against the reporter's exact error text before the fix, GREEN after. |
||
|
|
ec5b24b986 |
fix(providers): copilot-m365-web fails loudly on empty turns + tier-aware enterprise invocation (#7858, #7870) (#7958)
#7858 — accumulateBotContent() silently returned an empty delta for any unrecognized frame shape, and finish() only had a fallback for the type:2 finalResultMessage case; a turn with no content in ANY known shape closed with a bare `stop` + `[DONE]`, indistinguishable from a genuine empty answer. finish() now emits a sanitized error (Hard Rule #12) naming the resolved tier and the likely causes, and unrecognized update-frame shapes are logged by argument KEY only (never content, tokens, or cookies). #7870 — the enterprise tier only changed buildWsUrl() query params; buildChatInvocation() always fell back to the consumer M365_DEFAULT_OPTION_SETS (which declares the MSA-only enable_msa_user flag) and tone:"". resolveConnectionParams()/resolveTierOverrides() now also resolve and surface the tier itself, threaded through wsChat() -> sendChat() -> buildChatInvocation() via a new resolveChatInvocationOverrides() helper, so an enterprise-tier invocation declares the enterprise_*/bizchat_* option sets, the wider allowedMessageTypes captured from the real enterprise HAR (Discussion #7850), and tone:"Magic" — while individual and EDU payloads stay byte-identical to today. Regression tests: tests/unit/copilot-m365-web-silent-empty-7858.test.ts, tests/unit/copilot-m365-enterprise-invocation-7870.test.ts. |
||
|
|
5996acdcc7 | fix(providers): treat unreliable web-cookie /models probe status as unsupported, not valid (#7857) (#7959) | ||
|
|
25499bf94d |
fix(quality): tolerate ESLint's trailing unpruned-suppressions text in validate-release-green (#7837) (#7962)
Root cause: validate-release-green.mjs's ESLint gate runs `npx eslint . --format json --suppressions-location ...` without --pass-on-unpruned-suppressions. ESLint 9.x prints the valid JSON report to stdout first, then (if the suppressions file has any stale entries) appends 'There are suppressions left that do not occur anymore...' to stderr and exits 2. The gate concatenates stdout+stderr, so parseEslintJson() received valid JSON immediately followed by that sentence, JSON.parse() threw, and the caller reported the generic "could not parse eslint json" HARD failure instead of the real (harmless) unpruned-suppressions housekeeping condition. Fix: add --pass-on-unpruned-suppressions to the gate's eslint invocation (unpruned suppressions are release-time housekeeping, not a contributor defect), and harden parseEslintJson() with a bracket-depth scan so it recovers the JSON array even when trailing non-JSON text is glued on. Regression test: tests/unit/validate-release-green.test.ts — 'parseEslintJson tolerates ESLint's trailing unpruned-suppressions stderr sentence (#7837)', feeding parseEslintJson() the exact byte shape ESLint's own cli.js produces in this scenario. Confirmed RED before the fix (parsed === null), GREEN after. Gates run: node --import tsx/esm --test tests/unit/validate-release-green.test.ts (20/20 pass), npm run typecheck:core (clean), eslint --suppressions-location on changed files (clean), check-complexity.mjs + check-cognitive-complexity.mjs (OK, no regression), check-mutation-test-coverage.mjs --strict (no drift), check-changelog-integrity.mjs (OK), check-file-size.mjs (OK, no frozen files grown). Closes #7837 |
||
|
|
fe94529306 | fix(api): add amazon-q to the static model catalog (#7820) (#7960) | ||
|
|
c1bdd91e7b |
Hide internal reasoning replay placeholders (#7912)
* fix: hide internal reasoning replay placeholder * fix: hide internal reasoning replay placeholder in OpenAI→Claude translation too Mirror the isInternalReasoningPlaceholder() guard already applied to the Responses-API and OpenAI-Responses reasoning paths in openaiToClaudeResponse() (open-sse/translator/response/openai-to-claude.ts). The internal reasoning-replay sentinel was still leaking into the Claude "thinking" content block on this translation path. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test: close both-add merge of #7912 and #7905 reasoning/tool tests 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> |
||
|
|
1175746d4f |
fix(antigravity): collect native functionCall parts in SSE collector (#7902)
Rebuilt clean on release/v3.8.49 (branch carried old-main drift) — applies only the 2 real commits' delta: SSE collector now captures native functionCall parts in non-streaming, plus the test (typed emptyCollected() as AntigravityCollectedStream). Co-authored-by: Wital <wital@example.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
eadcbea1c9 |
fix(combo): strip boolean reasoning field for opencode-go providers (#7891)
* fix(combo): strip boolean reasoning field for opencode-go providers opencode-go backed providers (ollama-cloud, opencode-go, opencode, opencode-zen) use a Go ChatCompletionRequest struct where the reasoning field is typed as openai.Reasoning (a structured type). When a client sends reasoning: true or reasoning: false — valid per the OpenAI API — the Go JSON decoder rejects it with: 400: json: cannot unmarshal bool into Go struct field ChatCompletionRequest.reasoning of type openai.Reasoning This strips the boolean reasoning field before forwarding to these providers, allowing the upstream to apply its own default reasoning behavior. Object/string forms are left untouched. Observed in production: 3 consecutive 400 errors from ollama-cloud/glm-5.2 in a 30-second window, each with the unmarshal error. * fix(opencode): add null/primitive guard in stripBooleanReasoning Adds defensive check for null, undefined, and non-object inputs as suggested in review. Added unit test coverage for these edge cases. |
||
|
|
387ebc3e41 | fix(dashboard): safely render structured error objects in Request Logs detail (#7845) (#7920) | ||
|
|
583d3ebe1d | fix(providers): read reasoning_text in Claude-format response translator (#7856) (#7919) | ||
|
|
b15f343e67 | fix(providers): treat public-host 302 as valid in Gemini Web connection test (#7859) (#7917) | ||
|
|
62cbbcd2c0 |
fix(dashboard): preserve quota cutoff drafts (#7909)
Co-authored-by: Bryan Nathan <bryan@users.noreply.github.com> |
||
|
|
d3f8bbe555 | fix(sse): recover invalid Anthropic thinking signatures once (#7906) | ||
|
|
6b59e814da |
Restore Responses API custom tool calls (#7905)
* fix: restore Responses custom tool calls * fix: preserve nested Responses custom tool calls * fix: reset superseded tool call state * fix: preserve tool precedence and buffered Responses tool arguments * test: verify top-level tool descriptions take precedence * fix: preserve custom Responses tool streaming semantics * test: cover declared custom tool streaming round trips * test: cover Responses custom tool metadata collection * test: cover active Responses custom tool stream * test: isolate Responses active stream regression * fix: complete Responses custom tool round trips |
||
|
|
10823bcf83 |
fix(dashboard): repair monaco deep import broken by 0.56 exports map (#7897) (#7922)
monaco-editor 0.56.0 (bumped in #7897) ships a restrictive `exports` map ("./*.js" -> "./esm/vs/*.js", "./*" -> "./esm/vs/*.js") that rewrites every subpath by prepending esm/vs/. The pre-0.56 deep import `monaco-editor/esm/vs/editor/editor.api` therefore resolved to the doubled, non-existent `esm/vs/esm/vs/editor/editor.api.js` and broke the production Turbopack build (Module not found in MonacoEditor.tsx). Switch to the 0.56-compatible specifier `monaco-editor/editor/editor.api.js`, which resolves to the same file (esm/vs/editor/editor.api.js) as before. Adds tests/unit/monaco-editor-import-path.test.ts as a regression guard: asserts the specifier has no esm/vs/ prefix and resolves to editor.api.js against the installed monaco 0.56. |
||
|
|
3ab66e0ec0 |
deps: bump the development group with 5 updates (#7898)
Bumps the development group with 5 updates: | Package | From | To | | --- | --- | --- | | [@tailwindcss/postcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss) | `4.3.2` | `4.3.3` | | [c8](https://github.com/bcoe/c8) | `11.0.0` | `12.0.0` | | [lint-staged](https://github.com/lint-staged/lint-staged) | `17.0.8` | `17.1.0` | | [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.2` | `4.3.3` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.64.0` | `8.65.0` | Updates `@tailwindcss/postcss` from 4.3.2 to 4.3.3 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.3/packages/@tailwindcss-postcss) Updates `c8` from 11.0.0 to 12.0.0 - [Release notes](https://github.com/bcoe/c8/releases) - [Changelog](https://github.com/bcoe/c8/blob/main/CHANGELOG.md) - [Commits](https://github.com/bcoe/c8/compare/v11.0.0...v12.0.0) Updates `lint-staged` from 17.0.8 to 17.1.0 - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.0.8...v17.1.0) Updates `tailwindcss` from 4.3.2 to 4.3.3 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.3/packages/tailwindcss) Updates `typescript-eslint` from 8.64.0 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: "@tailwindcss/postcss" dependency-version: 4.3.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development - dependency-name: c8 dependency-version: 12.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: development - dependency-name: lint-staged dependency-version: 17.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development - dependency-name: tailwindcss dependency-version: 4.3.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development - dependency-name: typescript-eslint dependency-version: 8.65.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
8158d170d7 |
deps: bump the production group with 8 updates (#7897)
Bumps the production group with 8 updates: | Package | From | To | | --- | --- | --- | | [@aws-sdk/client-bedrock-runtime](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-bedrock-runtime) | `3.1088.0` | `3.1090.0` | | [@lobehub/icons](https://github.com/lobehub/lobe-icons) | `5.13.0` | `5.14.0` | | [@toon-format/toon](https://github.com/toon-format/toon) | `2.3.0` | `2.3.1` | | [ink](https://github.com/vadimdemedes/ink) | `7.1.0` | `7.1.1` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.24.0` | `1.25.0` | | [monaco-editor](https://github.com/microsoft/monaco-editor) | `0.55.1` | `0.56.0` | | [smol-toml](https://github.com/squirrelchat/smol-toml) | `1.6.1` | `1.7.0` | | [undici](https://github.com/nodejs/undici) | `8.7.0` | `8.8.0` | Updates `@aws-sdk/client-bedrock-runtime` from 3.1088.0 to 3.1090.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-bedrock-runtime/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1090.0/clients/client-bedrock-runtime) Updates `@lobehub/icons` from 5.13.0 to 5.14.0 - [Release notes](https://github.com/lobehub/lobe-icons/releases) - [Changelog](https://github.com/lobehub/lobe-icons/blob/master/CHANGELOG.md) - [Commits](https://github.com/lobehub/lobe-icons/compare/v5.13.0...v5.14.0) Updates `@toon-format/toon` from 2.3.0 to 2.3.1 - [Release notes](https://github.com/toon-format/toon/releases) - [Commits](https://github.com/toon-format/toon/compare/v2.3.0...v2.3.1) Updates `ink` from 7.1.0 to 7.1.1 - [Release notes](https://github.com/vadimdemedes/ink/releases) - [Commits](https://github.com/vadimdemedes/ink/compare/v7.1.0...v7.1.1) Updates `lucide-react` from 1.24.0 to 1.25.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.25.0/packages/lucide-react) Updates `monaco-editor` from 0.55.1 to 0.56.0 - [Release notes](https://github.com/microsoft/monaco-editor/releases) - [Changelog](https://github.com/microsoft/monaco-editor/blob/main/CHANGELOG.md) - [Commits](https://github.com/microsoft/monaco-editor/compare/v0.55.1...v0.56.0) Updates `smol-toml` from 1.6.1 to 1.7.0 - [Release notes](https://github.com/squirrelchat/smol-toml/releases) - [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0) Updates `undici` from 8.7.0 to 8.8.0 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v8.7.0...v8.8.0) --- updated-dependencies: - dependency-name: "@aws-sdk/client-bedrock-runtime" dependency-version: 3.1090.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: "@lobehub/icons" dependency-version: 5.14.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: "@toon-format/toon" dependency-version: 2.3.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: ink dependency-version: 7.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: lucide-react dependency-version: 1.25.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: monaco-editor dependency-version: 0.56.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: smol-toml dependency-version: 1.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: undici dependency-version: 8.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@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> |
||
|
|
cec2f58c24 |
chore(quality): prune stale ESLint suppression (db-migration-runner-account-identity)
#7843's test reorganization removed the 2 no-explicit-any usages that this suppression covered, leaving a stale entry that fails lint:json --max-warnings 0 (exit 2, 'suppressions left that do not occur anymore') on the release tip for every fresh PR run. Pruning tightens the gate — no rebaseline. |
||
|
|
161de7de4a |
fix(opencode-plugin): support separate management read token (#7885)
* fix(opencode-plugin): separate management read token * fix(opencode-plugin): scope inference auth and snapshots * fix(opencode-plugin): scope auth to base path * docs(changelog): format opencode management-read-token fragment as a bullet 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> |
||
|
|
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> |
||
|
|
ccdbc89290 |
feat(qwen): add Qwen3.8 Max Preview catalogs [Part 2/3] (#7874)
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> |