Commit Graph

1717 Commits

Author SHA1 Message Date
diegosouzapw
18b615f5f0 feat(quota): show per-account upstream quota in the pool card
Adds a read-only AccountQuotaRow component that fetches cached quota data
from GET /api/usage/provider-limits (same endpoint as ProviderLimits.tsx)
and renders a compact per-connection quota summary (% remaining + reset
countdown) inside each PoolCard, below the dimensions section and before
the BurnRateChart. Fail-softs to "—" on error / empty / loading state.
2026-05-31 11:21:25 -03:00
diegosouzapw
d4045e74b5 fix(models): honor per-model targetFormat for custom models (#2905)
Custom models (added via the UI) on opencode-go / openai-compatible nodes always
routed as OpenAI-compatible because there was no per-model targetFormat: addCustomModel
didn't accept it, the API schema stripped it, and getModelTargetFormat (static-registry
only) never saw it — so a custom model needing the Anthropic Messages shape fell back to
the provider default 'openai'.

Thread an optional targetFormat through addCustomModel / replaceCustomModels /
updateCustomModel + the providerModelMutationSchema + the POST/PUT route, surface it from
getModelInfo (one combined custom-model metadata lookup alongside apiFormat), and use it
in chatCore's targetFormat resolution before the provider default.

Closes #2905
2026-05-31 11:17:52 -03:00
diegosouzapw
d490a30b58 feat(quota): pool budget = per-account limit × account count (summed balde)
A pool with N same-type connections now has an effective budget of
perAccountLimit × N per dimension. Only the limit fed to fair-share
scales — consumption (pool-keyed shared bucket) is unchanged.
2026-05-31 11:09:25 -03:00
diegosouzapw
4157fbe7a9 feat(quota): balance + failover across same-provider accounts (N-step fill-first combo)
Replace the (connId × modelId) upsert loop in syncQuotaCombos with a
model-grouped build: one combo per model with ALL connections as steps
and strategy "fill-first", fixing the collision where a second same-provider
connection overwrote the first (last upsert won, only one account ever used).
2026-05-31 11:02:18 -03:00
diegosouzapw
17b58f8f5f fix(providers): route Pollinations to gen.pollinations.ai/v1 (#2987)
Pollinations retired the legacy text.pollinations.ai host, which now returns
404 'This is our legacy API' for all models. The registry primary baseUrl (and
the executor's hardcoded fallback) still pointed at it; gen.pollinations.ai/v1
is the current OpenAI-compatible gateway (it was already listed as the
secondary fallback). Make gen the sole/primary endpoint and align the executor
test.

Closes #2987
2026-05-31 10:56:21 -03:00
diegosouzapw
2521c09119 feat(quota): one provider per pool (block mixed-type) — server guard + wizard filter
- Add assertSingleProvider() helper in quotaPools.ts: queries provider_connections
  with DISTINCT provider WHERE id IN (...), throws if >1 provider detected.
- Call guard early in createPool (when connectionIds.length > 1) and updatePool
  (when input.connectionIds.length > 1), before any DB writes.
- Add lockedProvider useMemo in PoolWizard.tsx: derived from first selected
  connection; availableConnections filtered to same provider once locked.
- Show wizardSingleProviderNote i18n hint in step 1 when lockedProvider is set.
- Add wizardSingleProviderNote to en.json + pt-BR.json (parity).
- New test: tests/unit/quota-pool-single-provider.test.ts (4 cases).
- Update tests/unit/quota-multiprovider.test.ts: all D2 tests now use two
  same-provider connections (both PROVIDER_A/openrouter) — the tests were
  exercising connection-plumbing (scope, enforce membership, combo fan-out),
  not mixed-provider behavior per se; updated to remain valid under Task 3 rule.
2026-05-31 10:53:02 -03:00
diegosouzapw
bbc4c575e0 feat(quota): explain key-enable + exclusive behavior in concept card 2026-05-31 10:41:22 -03:00
diegosouzapw
767fad3d5c feat(quota): 2-column responsive grid for pool cards 2026-05-31 10:38:18 -03:00
Diego Rodrigues de Sa e Souza
009ad13a91 Merge pull request #2965 from soyelmismo/fix/oom-memory-leak-caches
fix(oom): prevent unbounded memory growth causing heap OOM crashes
2026-05-31 10:32:29 -03:00
Diego Rodrigues de Sa e Souza
c4a359ef5f Merge pull request #2964 from S0yora/feat/trae-solo-provider
feat(oauth): add Trae SOLO provider
2026-05-31 10:31:40 -03:00
Mcdowell Terence
c455a360ab fix(proxy): ignore null Proxifly API entries 2026-05-31 15:21:21 +02:00
diegosouzapw
d0a87970c6 fix(codex): drop image_generation for free-plan accounts (#2980 spin-off)
Free-plan Codex accounts (workspacePlanType === "free", from the OAuth id_token)
cannot run the server-side image_generation hosted tool, but the Codex CLI
injects it into every Responses request — causing an upstream 400. normalizeCodexTools
now drops image_generation when the connection is a free-plan account; paid plans
keep it. Mirrors CLIProxyAPI's isCodexFreePlanAuth guard.

Found during the #2980 (Codex OAuth compat) analysis against CLIProxyAPI.
2026-05-31 09:45:38 -03:00
Mcdowell Terence
8e9b3e217a fix(deps): remove proxifly package dependency 2026-05-31 14:43:11 +02:00
diegosouzapw
8151c46139 fix(dashboard): resolve custom provider display name across surfaces (#2968)
Custom providers (openai-compatible-<uuid> / anthropic-compatible-<uuid>) showed
the raw UUID id instead of the user-given node name in the active-requests panel,
proxy logger, and home-page provider topology. Only RequestLoggerV2 resolved it
(via a local helper + /api/provider-nodes fetch).

Extract the resolver into a shared util (src/shared/utils/providerDisplayLabel.ts,
unit-tested), reuse it in RequestLoggerV2, and apply it (with a provider-nodes
fetch) in ActiveRequestsPanel, ProxyLogger, and the HomePageClient topology so all
surfaces show the user-defined provider name.

Closes #2968
2026-05-31 09:34:16 -03:00
diegosouzapw
4b6d6c7670 fix(docker): honor OMNIROUTE_MEMORY_MB heap limit in standalone launcher (#2939)
The Docker image bakes NODE_OPTIONS=--max-old-space-size=256, and the standalone
launcher (scripts/dev/run-standalone.mjs, the Docker CMD) spawned 'node
server.js' without overriding it — so the server inherited the 256 MB cap and
OOMed randomly under load or with a large SQLite DB. `omniroute serve` already
honored OMNIROUTE_MEMORY_MB but the Docker path did not.

Add a shared resolveMaxOldSpaceMb() helper (OMNIROUTE_MEMORY_MB, default 512,
clamped [64,16384]) and have the launcher append --max-old-space-size to the
child NODE_OPTIONS (a trailing flag wins, overriding the baked 256 without
clobbering other flags). Update the .env.example doc to reflect the 512 default.
2026-05-31 09:15:55 -03:00
diegosouzapw
f5d74cd76d fix(routing): honor client reasoning.effort for gpt-5.5 + route suffixed variants to codex (#2877)
(A) For a Codex-only account, a bare gpt-5.5 Responses request was rerouted to
codex with the model hardcoded to gpt-5.5-medium (chatHelpers.ts). The Codex
executor reads a model-name suffix as an explicit modelEffort that, per #2331,
overrides the client's reasoning.effort — so a genuine reasoning.effort=xhigh
was silently demoted to medium. Keep the bare gpt-5.5 id (the connection
fallback still supplies the default effort); the executor precedence is
untouched, so #2331 stays intact.

(B) gpt-5.5-xhigh/-high/-low misrouted to the openai provider (only bare gpt-5.5
was codex-preferred), so codex-only users got 'No credentials for provider:
openai'. Add the suffixed variants to CODEX_PREFERRED_UNPREFIXED_MODELS so they
infer codex before the /^gpt-/ → openai fallback.

Closes #2877
2026-05-31 09:05:35 -03:00
diegosouzapw
1a701292b6 fix(sse): remove duplicate const settings in handleChatCore
handleChatCore declared `const settings = cachedSettings ?? await
getCachedSettings()` twice in the same function scope — the consolidated
"fetch once, reuse" const near the top, plus a duplicate added alongside the
per-key stream-default-mode feature. esbuild/tsx rejected the same-scope
redeclaration ("The symbol 'settings' has already been declared"), which made
every unit test that imports chatCore fail to transform and broke the
production build.

Remove the duplicate; reuse the earlier consolidated `settings`. Add an
import-guard regression test.
2026-05-31 09:00:30 -03:00
diegosouzapw
cf9a78ad32 fix(quota): unwrap pool usage response so quota-share page renders pools with allocations
GET /api/quota/pools/[id]/usage returns { usage: snapshot }, but usePoolUsage
stored the whole wrapper, leaving usage.dimensions undefined. StackedAllocationBar
then dereferenced usage.dimensions[dimensionIndex] (undefined[0]) and crashed the
entire quota-share page for any pool that has allocations. Unwrap data.usage in the
hook + defensively guard usage.dimensions?.[i] / dim.perKey ?? [] in the bar.
2026-05-31 08:39:05 -03:00
oyi77
a365706d65 feat: add pool support to DuckDuckGo Web and LLM7 providers
- duckduckgo-web.ts: Add poolConfig (2-5 sessions, 1s cooldown)
- providerRegistry.ts: Add poolConfig field to RegistryEntry type, configure for llm7
- default.ts: Read poolConfig from registry in constructor

DuckDuckGo Web now uses pool for VQD token rotation.
LLM7.io now uses pool with 2s cooldown for its 1 req/s rate limit.
2026-05-31 07:05:21 -03:00
oyi77
71bfe084ae refactor: make SessionPool modular & provider-agnostic
Move pool integration from Pollinations-specific code into BaseExecutor
so any executor can opt in via a protected poolConfig property.

Changes:
- base.ts: Add poolConfig, getPool(), buildPoolHeaders() to BaseExecutor
- pollinations.ts: Remove static pool, use base class pattern
- session-pool-modular.test.ts: 36 tests for provider-agnostic pool behavior

Closes #2953
2026-05-31 07:05:21 -03:00
diegosouzapw
209d0b5ae4 test(build): expect app/peer-stamp.mjs in pack-artifact required paths 2026-05-31 07:00:08 -03:00
Xiangzhe
778f74c4cb feat(providers): add SiliconFlow endpoint selector
Signed-off-by: Xiangzhe <xiangzhedev@gmail.com>
2026-05-31 13:10:33 +08:00
diegosouzapw
a06054ad34 fix(translator): drop orphan tool results from empty call_id (#2893)
Codex can emit a function_call with an empty/missing call_id; the matching
function_call_output then becomes a role:'tool' message with no preceding
tool_call, which the upstream rejects: "Messages with role 'tool' must be a
response to a preceding message with 'tool_calls'". The orphan filter let it
slip through because its guard (`&& rec.tool_call_id`) short-circuited on the
empty id.

Two changes in openaiResponsesToOpenAIRequest:
- Skip function_call items with an empty call_id (mirrors the empty-name skip),
  so no dangling assistant tool_call with an unmatched id is emitted.
- Drop ANY role:'tool' message whose tool_call_id (including empty/missing) has
  no matching tool_call, instead of only those with a truthy id.

Closes #2893
2026-05-31 01:52:24 -03:00
diegosouzapw
380d558586 fix(auth): opencode-zen falls back to anonymous no-auth when no key (#2962)
opencode-zen serves the public, signup-free OpenCode Zen endpoint
(https://opencode.ai/zen/v1). With no API-key connection configured,
getProviderCredentials returned null, so the Playground/combos surfaced
"No credentials for provider: opencode-zen" when selecting an OpenCode free
model.

Fall back to synthetic no-auth credentials for opencode-zen when no usable
connection exists (a configured active key is still selected first; a
rate-limited/terminal key returns its own signal before this point). Other
api-key providers are unaffected.

Closes #2962
2026-05-31 01:48:31 -03:00
diegosouzapw
eb7348aeb9 fix(resilience): route-restriction 403 must not mark a connection unavailable (#2929)
Fireworks Fire Pass (fpk_*) keys return 403 "Fire Pass API keys are not
authorized for this route." on /models while still serving chat. Two paths
wrongly penalized the key:

- validateOpenAILikeProvider returned "Invalid API key" for any 403 on the
  models endpoint without trying the chat probe.
- checkFallbackError classified the 403 as AUTH_ERROR (retryable cooldown), and
  even a generic fall-through hit the transient-cooldown default — marking the
  connection unavailable.

Fix: validateOpenAILikeProvider now inspects the 403 body and falls through to
the chat probe for "not authorized for this route" responses (401 and generic
403 still fail fast). checkFallbackError short-circuits such route-restriction
403s to { shouldFallback: false, cooldownMs: 0 } so the connection is not cooled
down. Per CLAUDE.md, a generic api-key 403 should be recoverable unless terminal.

Closes #2929
2026-05-31 01:38:05 -03:00
diegosouzapw
e63fbed64b fix(combo): no-auth OpenCode combos use oc/ prefix, not opencode/ (#2901)
The no-auth OpenCode provider has id "opencode" and alias "oc". The combo
builder built qualifiedModel from the provider id ("opencode/big-pickle"), but
parseModel("opencode/...") resolves to the opencode-zen api-key tier via a
manual ALIAS_TO_PROVIDER_ID override — not the no-auth "opencode" provider.
"oc/<model>" resolves correctly.

Rewrite qualifiedModel to the provider alias for no-auth providers (only when
the alias differs from the id), keeping providerId for getModelIsHidden. Aligned
the route test that previously asserted the buggy "opencode/" prefix.

Closes #2901
2026-05-31 01:20:57 -03:00
diegosouzapw
729252008b fix(authz): close 2nd Host-spoof path (cliTokenAuth) + IPv6 loopback + Docker stamp
Adversarial review of the peer-IP fix surfaced: (1) CRITICAL — cliTokenAuth.ts
still derived loopback from new URL(request.url).hostname (the same spoofable
Host class), letting a remote caller with a stolen CLI token reach management
APIs via Host: 127.0.0.1; now it trusts the middleware-stamped locality verdict
(AUTHZ_HEADER_PEER_LOCALITY, a client-stripped trusted header). (2) HIGH —
isLoopbackHost mangled bare IPv6 (::1, ::ffff:127.0.0.1) via split(":")[0],
a fail-closed DoS on IPv6 deploys. (3) HIGH — the Docker entrypoint ran bare
server.js (no peer stamp); run-standalone.mjs now prefers server-ws.mjs.
2026-05-31 01:15:27 -03:00
diegosouzapw
b5d03ed3f2 fix(translator): drop Codex image_generation tool in Responses→Chat (#2950)
Codex Desktop injects an image_generation hosted tool into every Responses API
request (even text-only ones). The tool-type validator threw
unsupportedFeature() (400) for it, breaking every Codex Desktop request.

Mirror the tool_search handling: add IMAGE_GENERATION_TOOL_TYPES, allow it past
the validator guard, and drop it from the tools array before forwarding to Chat
Completions.

Closes #2950
2026-05-31 01:05:13 -03:00
diegosouzapw
f14722315d fix(providers): route Copilot Claude/Gemini via chat/completions (#2911)
The github provider has format:"openai" (baseUrl .../chat/completions) plus a
separate responsesBaseUrl (.../responses); a model only uses the Responses API
when it sets targetFormat:"openai-responses". GitHub Copilot's Responses API
does not serve Claude/Gemini models, so claude-opus-4.7, claude-opus-4-5-20251101,
gemini-3.1-pro-preview and gemini-3-flash-preview failed with [400]. The working
claude-opus-4.6 carries no targetFormat and uses chat/completions.

Drop targetFormat:"openai-responses" from those four Claude/Gemini entries so
they use the provider default. Native OpenAI gpt-* models (and oswe-vscode-prime)
keep the Responses API.

Closes #2911
2026-05-31 00:56:41 -03:00
diegosouzapw
0a09fa5a11 fix(authz): trust real TCP peer IP stamp over spoofable Host header for LOCAL_ONLY gate
The middleware runtime exposes no socket, so a prior fix derived LOCAL_ONLY
locality from the Host header — letting a remote caller send Host: 127.0.0.1
and reach spawn-capable routes (RCE class). The custom Node servers now stamp
the real socket.remoteAddress into a token-signed internal header; the policy
trusts only a stamp whose token matches this process's secret, and fails closed
otherwise. Preserves the owner-authorized loopback + private-LAN access without
trusting any client-controlled header.
2026-05-31 00:43:48 -03:00
diegosouzapw
672398e86f fix(routing): replay reasoning_content for OpenCode big-pickle (#2900)
big-pickle's OpenCode/Zen upstream runs DeepSeek thinking mode, but the model
id reveals no DeepSeek signal, so requiresReasoningReplay (called with
allowLegacyFallback:false) never triggered. Follow-up/tool-use turns failed
with [400] 'The reasoning_content in the thinking mode must be passed back to
the API'. Note: requiresReasoningReplay does not consume supportsReasoning, so
the registry flag alone would not have fixed it.

Add RegistryModel.interleavedField (mirrors models.dev interleaved_field),
declare interleavedField:'reasoning_content' (+ supportsReasoning:true) on
big-pickle in both opencode and opencode-zen, and surface the registry value
in getResolvedModelCapabilities so requiresReasoningReplay returns true.

Closes #2900
2026-05-31 00:41:46 -03:00
diegosouzapw
d5b163558d fix(db): resolve 077 migration version collision blocking getDbInstance
077_api_key_stream_default_mode.sql and 077_quota_pools.sql both claimed
prefix 077, so getMigrationFiles() threw a version-collision error and
getDbInstance() failed at every startup (app would not boot; all DB-touching
unit tests were red on release/v3.8.8).

Renumber the dependency-free, idempotent quota_pools migration 077 -> 085
(no other migration references quota_pools/quota_allocations), keep the
non-idempotent api_key_stream_default_mode ALTER at 077, add a retroactive
isSchemaAlreadyApplied guard (case 085) for DBs that already applied it under
077, and add a regression test enforcing unique migration prefixes.
2026-05-31 00:33:51 -03:00
soyelmismo
74ba399e04 fix: address Gemini code review on OOM fix PR #2965
1. check-permissions.sh: swap NODE_OPTIONS flag order so runtime
   OMNIROUTE_MEMORY_MB override wins (last flag takes precedence)

2. comboMetrics.ts: evictOldestMetric(targetMap) now accepts the
   target map as parameter; cleanup timer iterates BOTH metrics
   AND shadowMetrics; null lastUsedAt falls back to Date.now()
   instead of epoch 0 (prevents premature eviction of intent-only
   entries)

3. providerRegistry.ts: remove Proxy wrapper — adds CPU/complexity
   overhead with zero memory savings since _REGISTRY_EAGER is
   already fully allocated and generator functions iterate all
   entries at startup. Simple re-export instead.

4. usage.ts: use per-cache TTL constants in cleanup timer
   (ANTIGRAVITY_MODELS_CACHE_TTL_MS=1min, others=5min) instead of
   single 10min SUB_CACHE_TTL_MS for all caches.

5. Add 18 unit tests for comboMetrics memory management covering:
   basic CRUD, shadow metrics, intent tracking, eviction at capacity,
   strategy tracking, and reset operations.
2026-05-30 22:13:59 -05:00
diegosouzapw
b93cde7507 fix(quota): wire multi-provider icons into pool card (D3 dead-code gap) 2026-05-31 00:09:52 -03:00
diegosouzapw
acd517eb1e feat(quota): multi-connection pool wizard — select N providers, all models available (Phase D3)
- PoolCreateSchema: add optional connectionIds[] + .refine() that enforces primary membership
- PoolWizard: replace single-select dropdown with checkbox multi-select; first checked = primary (badge); step-2 adds helper note for additional connections; step-3 preview grouped by provider with +N more; POST body sends both connectionId and connectionIds
- PoolCard: optional providers[] prop renders a row of ProviderIcon (up to 3 + badge) instead of a single icon when pool has multiple connections
- i18n: 4 new keys added to both en.json and pt-BR.json (wizardConnectionsLabel, wizardPrimaryBadge, wizardAdditionalConnectionsNote, wizardPreviewMoreModels) — parity maintained (23 wizard keys each)
- Tests: quota-pool-wizard-multi.test.ts (21 tests) covering schema accept/reject, structural wizard assertions, and i18n parity
2026-05-30 23:03:21 -03:00
diegosouzapw
e5a624d0ec feat(quota): propagate N pool connections through scope, combos, enforce (Phase D2)
- quotaKey.ts (resolveQuotaKeyScope): iterate pool.connectionIds (fall back to
  [connectionId] for un-backfilled rows); each connection contributes its own
  connId + provider to the scope. poolSlugs logic unchanged (one slug per pool).
- quotaCombos.ts (syncQuotaCombos): replace single-connection resolvePoolProvider
  with resolvePoolForSync that returns all connectionIds; iterate each connId to
  build the desiredNames union across all providers; upsert combos pinned to the
  CORRECT per-connection connId; prune against the full union so only truly stale
  combos (no longer produced by any current connection) are deleted.
- enforce.ts (enforceQuotaShare + recordConsumption): both pool-matching loops
  changed from equality (p.connectionId === input.connectionId) to membership
  (p.connectionIds.includes) with fallback for un-backfilled rows. Fail-open
  (B16) and pool-level dimension key semantics are preserved unchanged.
- quotaPools.ts: no logic change needed — connectionIds already flows through
  getPool (D1); syncQuotaCombosGuarded passes poolId and syncQuotaCombos
  resolves the full QuotaPool internally.
- tests/unit/quota-multiprovider.test.ts: 6 new tests covering D2 (scope,
  enforce primary/secondary membership, combos 2-provider create + prune).
  All 22 tests pass (14 new + 8 existing enforce + pool-connections suites).
2026-05-30 22:44:50 -03:00
S0yora
8adc0a0d9a feat(oauth): add Trae SOLO provider (work/code modes)
Full SOLO remote-agent integration against solo.trae.ai's reverse-engineered
API (upgrades the previous import_token stub).

- open-sse/executors/trae.ts: streaming executor for the solo_agent_remote API
  (POST /chat_sessions + GET /events SSE -> OpenAI chat.completions), accumulating
  plan_item thoughts and mapping token_usage. Headless Cloud-IDE-JWT refresh via
  the ExchangeToken endpoint.
- Session mode via model id: `trae/work` runs the fast work-mode auto agent;
  `trae/auto` and named models (gpt-5.4, kimi-k2.5, gemini-3.1-pro, ...) run in
  code mode.
- Provider registry entry (models + 272k context) and executor wiring.
- Two credential paths: browser /authorize loopback callback (captures the JWT +
  long-lived refresh token) and manual Cloud-IDE-JWT paste via
  POST /api/oauth/trae/import (Zod-validated).
- TraeAuthModal dashboard UI wired into the provider detail page.
- mapTokens/providerSpecificData carry the SOLO common_params identity fields.
- Tests: executor (stream/non-stream/error/refresh/work-mode + callback parser)
  and updated oauth-trae provider tests.
2026-05-31 04:38:22 +03:00
diegosouzapw
428947207f test(quota): align sidebar-costs-section to 4 items after C2 Plans retirement 2026-05-30 22:35:45 -03:00
diegosouzapw
69acd664d7 feat(quota): pool can span N connections — quota_pool_connections join table (Phase D1)
Adds migration 086 to create the `quota_pool_connections` join table with a backfill
that seeds every existing pool's single connection_id as its first member. Updates
QuotaPool type with `connectionIds: string[]`, wires createPool/updatePool/deletePool
to maintain the join table transactionally, and keeps `connection_id` as the primary
back-compat column synced to `connectionIds[0]`.
2026-05-30 22:32:02 -03:00
diegosouzapw
2300db6cc5 feat(quota): retire standalone Plans screen, unified into pool wizard (Phase C2)
Remove plans/ route and sidebar entry; PoolWizard Step 2 now covers plan-dimensions inline.
2026-05-30 22:15:27 -03:00
terence71-glitch
e2ad12d090 fix(proxy): show registry provider proxies in dashboard (#2963)
Integrated into release/v3.8.8
2026-05-30 22:14:53 -03:00
diegosouzapw
62de5a83b8 feat(quota): 3-step pool wizard unifying connection, plan and key allocation (Phase C1) 2026-05-30 22:08:41 -03:00
diegosouzapw
4536aabe23 Merge PR 2959 into release/v3.8.8 2026-05-30 22:02:49 -03:00
diegosouzapw
c90bed0043 Merge PR 2957 into release/v3.8.8 2026-05-30 22:01:07 -03:00
diegosouzapw
fa4bd6c68c Merge PR 2951 into release/v3.8.8 2026-05-30 22:01:04 -03:00
diegosouzapw
1a9b2bfd85 Merge PR 2943 into release/v3.8.8 2026-05-30 22:00:38 -03:00
diegosouzapw
266c145ee4 Merge PR 2940 into release/v3.8.8 2026-05-30 22:00:34 -03:00
diegosouzapw
8dafe78d79 Merge PR 2938 into release/v3.8.8 2026-05-30 22:00:31 -03:00
diegosouzapw
606b7092b6 Merge PR 2937 into release/v3.8.8 2026-05-30 22:00:29 -03:00
diegosouzapw
cf3600de11 Merge PR 2931 into release/v3.8.8 2026-05-30 22:00:20 -03:00