Commit Graph

4132 Commits

Author SHA1 Message Date
diegosouzapw
65195dcd7a docs(changelog): audit v3.8.8 — fill gaps, credit @branben/@JxnLexn, add contributor hall
Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: #3052 (heap-pressure auto-calibration),
  #3051/#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  #3049/#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (#2958 scope fix, #2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (#2966–#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
2026-06-01 21:59:18 -03:00
Diego Rodrigues de Sa e Souza
a5f3c998c1 fix(sse): auto-calibrate heap-pressure threshold to the V8 heap ceiling (#3052)
Threshold now derives from the live V8 heap ceiling (85%, floor 400MB) instead of a fixed 200MB that sat below the ~260MB app baseline and 503'd every request. Tracks --max-old-space-size across 1GB/2GB/large VPS.
2026-06-01 21:12:37 -03:00
terence71-glitch
08c70572fb fix(proxy): resolve registry assignments for combo and key levels (#3048)
* fix(proxy): resolve registry assignments for combo and key levels

* fix(proxy): guard registry scope level lookup
2026-06-01 19:47:46 -03:00
terence71-glitch
5350b5e7f5 fix(proxy): fail closed for OAuth usage account proxies (#3051) 2026-06-01 19:47:31 -03:00
Paijo
39a673b7d6 fix(pollinations/duckduckgo): wire session pool for fingerprint rotation (#3049)
* fix(pollinations): wire session pool + add idle pruning

PollinationsExecutor.getPool() returned null because poolConfig was
never set. Now sets DEFAULT_POOL_CONFIG in constructor so anonymous
requests get fingerprint rotation and 429 cooldown management.

Also:
- Use acquireBlocking() instead of acquire() to wait for available session
- Add startAutoPrune() for periodic idle session cleanup (5min idle timeout)
- Improve execute() error handling with proper finally block

* fix(duckduckgo-web): wire session pool for fingerprint rotation

DuckDuckGoWebExecutor had poolConfig set but never called getPool()
in execute(). Added session acquisition via acquireBlocking() and
merges fingerprint headers into fetch calls for rate limit evasion.

* fix: address PR #3049 review comments

- duckduckgo-web: fix session leak (add finally release), fix race
  condition (report status before release), remove duplicate sessionHeaders
- pollinations: fix race condition (move release to finally, report before)

* fix: address all PR #3049 review comments

- duckduckgo-web: add pool reporting on 429/500 early returns (was missing)
- duckduckgo-web: add sessionHeaders to retry fetch on 401/403
- sessionPool: add .unref() to setInterval to prevent keeping process alive

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
2026-06-01 19:46:58 -03:00
diegosouzapw
8f0615fd04 fix(security): resolve 5 CodeQL alerts, document FPs, harden deploy skills
Real fixes (alerts auto-close on next scan):
- agentSkills/generator: escape backslash before double-quote in YAML
  frontmatter so a trailing backslash can't escape the closing quote
  (js/incomplete-sanitization #297/#298)
- usage: replace /\s*\(RESTRICTED\)\s*$/ with a non-backtracking literal +
  trim (js/polynomial-redos #275)
- i18n/request: skip __proto__/constructor/prototype in deepMergeFallback
  (js/prototype-pollution-utility #274)
- scripts/ad-hoc/nvidia diag: log key presence only, never key chars
  (js/clear-text-logging #273)
- tests: regression coverage for all three production fixes (YAML round-trip,
  proto-pollution guard, RESTRICTED strip + ReDoS timing)

Docs:
- ARCHITECTURE.md: executor count 45->55, OAuth modules 15->16, agy.ts in list

Deploy skills (deploy-vps-*-cc/-cx/-ag): add --legacy-peer-deps (npm v11
peer-dep resolver crashes on the omniroute tree) and replace the
"; pm2 start" that masked a failed install with a proper && chain.
2026-06-01 19:36:48 -03:00
Paijo
2a8954663c fix(claude-web): inject cf_clearance into cookie for 403 prevention (#3046)
The execute() and testConnection() methods called
normalizeClaudeSessionCookie() which does NOT inject cf_clearance.
Cloudflare pins cf_clearance to TLS fingerprint — without it, requests
get challenged with 403 even with valid session cookies.

Changed both call sites to use normalizeClaudeSessionCookieWithAutoRefresh()
which auto-injects cf_clearance via Turnstile solver when missing.

Fixes: [403]: Claude Web API error:

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
2026-06-01 18:14:39 -03:00
diegosouzapw
a1c743ddb0 chore(release): v3.8.8 — changelog from v3.8.7, version sync, i18n + env-doc fixes
- bump package.json / open-sse / electron / openapi / llm.txt to 3.8.8
- restructure CHANGELOG: Unreleased -> [3.8.8], dedup broken Notion/MCP block (was 12x)
- add every PR since v3.8.7 that was missing: Quota Share Engine (#2859/#3022/#3032),
  page redesigns (#2827/#2839/#2847/#2849/#2869/#2873), and fixes #2960/#2973/#2984/
  #3021/#3029/#3031/#3035/#3036/#3037/#3039/#3043/#3028; folded #2978/#2988/#3041
- insert [3.8.8] section into all 41 i18n CHANGELOGs + sync llm.txt mirrors
- document OMNIROUTE_PLUGINS_ALLOW_EXEC in .env.example + ENVIRONMENT.md (env-doc-sync gap)
2026-06-01 17:11:21 -03:00
Paijo
9e7f3cad10 feat(plugins): plugin hook wiring + comprehensive test suite + welcome-banner example (#3045)
Follow-up to the plugins framework (#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
2026-06-01 16:20:11 -03:00
Diego Rodrigues de Sa e Souza
20c31493af feat(plugins): plugins framework + per-API-key disable-non-public-models (#3041)
Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
2026-06-01 15:43:55 -03:00
Paijo
89c52d4f04 fix: resolve pre-existing test failures (env sync, PII, quota, sidebar) (#3039)
Integrated into release/v3.8.8. 9 legit test alignments + 34 new test files kept; restored 5 masked assertions (sk_ key redaction, circular/SSN redaction, T24 wait-log, THEORY-004 SSE, relay handoff) to strict/meaningful checks. Thanks @oyi77!
2026-06-01 14:52:50 -03:00
Hernan Javier Ardila Sanchez
482e4690eb fix(dashboard): use lightweight ping endpoint for MaintenanceBanner (fixes #3040) (#3043)
Integrated into release/v3.8.8. Applied review fixes: moved the SELECT 1 into a pingDb() db helper (no raw SQL in route, Hard Rule #5) + the 503 catch no longer leaks err.message (Hard Rule #12). Thanks @herjarsa!
2026-06-01 14:31:42 -03:00
diegosouzapw
4e4e89759a chore: ignore local cache and workspace helper directories
Add local-only cache, IDE, mono-repo, references, and task directories to .gitignore to prevent development artifacts from being committed.
2026-06-01 13:35:29 -03:00
Chewji
6b7ae9ad2e fix(mcp): resolve streamable http transport readiness offline status (#3037)
Integrated into release/v3.8.8. Fixes MCP streamable-HTTP transport readiness reporting when offline + session sweep. Thanks @Chewji9875!
2026-06-01 09:59:23 -03:00
dangeReis
e11366b647 fix(privacy): resolve PII feature flag correctly and fix PII response sanitization in streaming SSE requests (#3021)
Integrated into release/v3.8.8. Fixes the PII feature-flag resolution + adds streaming-aware SSE PII sanitization (rolling-window transform, obfuscation-hardened regexes, Luhn/CPF/CNPJ checks). Applied review fixes: block-mode + fallback errors no longer leak pattern types / upstream err.message into stream errors (Hard Rule #12); typed the createPiiTransform cast (no any); fixed a string===boolean type error; dropped the generated .source/* files. Thanks @dangeReis!
2026-06-01 08:22:09 -03:00
Andrianata Daud
6561548687 fix(docker): warn-only on /app/data permission check, remove exit 1 (#3036)
Integrated into release/v3.8.8. Docker /app/data permission check is now warn-only (no exit 1 crash-loop on first run) + UID hint uses $(id -u):$(id -g). The mcp-tools-43.svg part was already in the release. Thanks @wussh!
2026-06-01 08:12:19 -03:00
Muhammad Tamir
b20748a73e Fix DuckDuckGo Missing Api Key & Update OpenCode Free Model List (#3008)
Integrated into release/v3.8.8. The DuckDuckGo noAuth bypass + OpenCode free-model refresh were already integrated earlier (you're credited in CHANGELOG); this squashes your branch in for the Merged badge. Thanks @NekoMonci12!
2026-06-01 08:11:23 -03:00
Insomnia
baa4e56997 fix: improve macOS Electron window chrome (#3029)
Integrated into release/v3.8.8. macOS Electron window-chrome: tray icon template + draggable header region (excludes interactive controls) with a safe fallback + MutationObserver cleanup. Squash-merged (drops the AI co-author trailers per repo policy). Thanks @bobbyunknown!
2026-06-01 08:08:01 -03:00
mi
57dfa25312 fix(oom): prevent per-request memory accumulation (256MB heap) (#2973)
Integrated into release/v3.8.8. OOM fix: truncateForLog caps logged bodies at 8KB (prevents multi-MB clone accumulation across log call-sites) + a heap-pressure 503 guard. Applied review fix: the 503 body no longer leaks the heap figure (Hard Rule #12) — logged internally instead. Thanks @soyelmismo!
2026-06-01 08:07:03 -03:00
Tentoxa
aa8033dae9 chore: bump Claude Code identity to 2.1.158 + sync anthropic-beta flags from live captures (#3010)
Integrated into release/v3.8.8 — free_tier_exhausted error rule (identity/beta-flags already in release). Thanks @Tentoxa!
2026-06-01 07:57:55 -03:00
CitrusIce
c34285f676 fix(stream): drop leaked chat bootstrap chunk for responses clients (#3035)
Integrated into release/v3.8.8. Drops the leaked empty chat.completion.chunk bootstrap frame before response.* SSE events so strict /v1/responses clients (OpenCode) don't fail on the first frame. Thanks @CitrusIce!
2026-06-01 07:56:25 -03:00
Yuriy Bilous
8ef8a9b5a7 fix(i18n): complete Ukrainian (uk-UA) UI translation coverage (#2988)
Integrated into release/v3.8.8. Applied your Ukrainian translations across the current uk-UA.json structure (709 keys improved from English/__MISSING__ to proper Ukrainian), preserving release's latest key set. Thanks @Lion-killer!
2026-06-01 07:55:03 -03:00
guanbear
dd42b1564f Fix missing API key scope translations (#3031)
Integrated into release/v3.8.8. Fills the 7 apiManager.* self-service scope keys (managementAccessDesc, selfServiceVisibility(+Desc), ownUsageVisibility(+Desc), sharedAccountQuotaVisibility(+Desc)) across 41 locales + regression test. Resolved conflicts against the current release on your branch. Thanks @guanbear!
2026-06-01 07:50:58 -03:00
Paijo
e4eeb6dc7f refactor: Make SessionPool modular & provider-agnostic (#2978)
Integrated into release/v3.8.8. Applied review fixes: typed DefaultExecutor.execute(input: ExecuteInput), guarded result?.response?.status, removed the unused cloakbrowser dependency. Session-pool modular refactor + 36 tests green. Thanks @oyi77!
2026-06-01 07:44:52 -03:00
Paijo
4de674e0fc test: increase coverage to >60% — 353 new test cases (#3018)
Integrated into release/v3.8.8 — 122 new passing tests (DB core/models/settings, executor base, stream payload collector, model resolver). Thanks @oyi77!
2026-06-01 07:39:09 -03:00
Diego Rodrigues de Sa e Souza
099cb67317 Merge pull request #3032 from diegosouzapw/feat/quota-share-v2
feat(quota): Quota Share v2 — nav move, 3-col grouped layout, endpoints+key preview, full pool edit
2026-06-01 07:03:14 -03:00
diegosouzapw
a332b659c2 Revert "chore(quota): remove orphaned groupAllocationNote i18n key (EditAllocationsModal retired)"
This reverts commit 6fd7098c21.
2026-06-01 03:27:06 -03:00
diegosouzapw
6fd7098c21 chore(quota): remove orphaned groupAllocationNote i18n key (EditAllocationsModal retired) 2026-06-01 03:24:57 -03:00
diegosouzapw
df7b1e83c2 feat(quota): available-endpoints card + per-key model preview 2026-06-01 03:22:54 -03:00
diegosouzapw
6848636351 feat(quota): edit button opens full PoolWizard (exclusive-preserving); retire EditAllocationsModal
- PoolWizard: add editPoolExclusive prop; pre-fill uses editPoolExclusive ?? false so editing an exclusive pool keeps it exclusive instead of silently clearing the flag
- QuotaSharePageClient: wire editing state to a second <PoolWizard editPool=... editPoolExclusive=...>; compute editingExclusive from apiKeys.allowedQuotas; remove EditAllocationsModal import and handleSaveAllocations
- Delete EditAllocationsModal.tsx (fully subsumed by PoolWizard step 3)
- Tests: quota-email-privacy + quota-groups-ui + quota-pool-wizard updated to reflect retirement; new quota-edit-opens-wizard.test.ts (9 source-scan assertions)
2026-06-01 03:15:21 -03:00
diegosouzapw
3c8e84d702 feat(quota): all-groups default + stacked group sections + 3-col cards
- selectedGroupId defaults to "all" instead of "group-demo"
- <select> prepends <option value="all">{t("allGroups")}</option>
- Pool list replaced by groupsToRender map: one stacked section per group
  (heading: group name + count via groupPools.length), each with a
  grid-cols-1 md:grid-cols-2 xl:grid-cols-3 card grid
- Rename button guard changed from selectedGroupId !== "group-demo" to
  selectedGroupId !== "all" (no single target when viewing all groups)
- i18n: allGroups added to en.json ("All groups") + pt-BR.json ("Todos os grupos")
- quota-share-layout-v2.test.ts: 10 source-scan + i18n parity assertions
- quota-groups-ui.test.ts: align group heading test to groupPools.length
2026-06-01 03:03:03 -03:00
Diego Rodrigues de Sa e Souza
46c482ca98 Merge PR 3030 into release/v3.8.8 2026-06-01 03:02:34 -03:00
diegosouzapw
ac5ac0c2a7 fix(api): guard rotating providers in manual token-refresh route (Codex family-revocation)
POST /api/providers/[id]/refresh was the last unguarded proactive-refresh
entry point for rotating-refresh providers. The dashboard auto-refreshes every
expiring connection on a page load (and an old cached frontend bulk-calls this
endpoint), so each Codex account's single-use refresh_token got rotated; Auth0
then revoked the whole token family (openai/codex#9648) — every account but the
last died with [403] <!DOCTYPE. refreshAndUpdateCredentials (quota-sync) and the
connection-test route were already guarded; this closes the gap.

The route now skips proactive rotation when rotationGroupFor(provider) !== null
and returns {success,skipped,message}, deferring genuine expiry to the reactive,
serialized 401 path. Non-rotating providers keep refreshing on demand.

Test: codex-manual-refresh-rotating-guard (source-assertion, matches the
token-refresh-race-comprehensive #2941 style).
2026-06-01 03:00:13 -03:00
diegosouzapw
b64489b3ad feat(quota): PoolWizard editPool mode (full pool edit via PATCH)
- Add `editPool?: QuotaPool` to PoolWizardProps; import QuotaPool type
- Open effect pre-fills state (connectionIds, name, groupId, allocations,
  plan dims) from editPool; create-reset unchanged when editPool absent
- handleFinish branches on editPool: create path keeps POST→PUT→PATCH
  unchanged; edit path sends a single PATCH (name+groupId+connectionIds+
  allocations+exclusive) then optional PUT when dimensionsEdited
- Title uses t("editPoolTitle"), submit button uses t("saveChanges") in edit mode
- Add editPoolTitle/saveChanges to en.json and pt-BR.json (quotaShare namespace)
- New source-scan test: quota-pool-wizard-edit.test.ts (15 assertions, all pass)
2026-06-01 02:52:51 -03:00
diegosouzapw
881a8e9b56 feat(quota): move Quota Share nav item under Provider Quota 2026-06-01 02:43:59 -03:00
diegosouzapw
342f12b77a feat(quota): GET /api/quota/keys/[id]/models — preview the qtSd/ models a key sees 2026-06-01 02:39:37 -03:00
diegosouzapw
e5392a7eaa fix(quota): pool PATCH prunes OLD group/provider combos before re-sync (no orphan qtSd/ on switch) 2026-06-01 02:35:26 -03:00
diegosouzapw
e694674851 feat(quota): pool PATCH accepts groupId + connectionIds, re-syncs combos on connection change 2026-06-01 02:28:21 -03:00
Diego Rodrigues de Sa e Souza
bf96769db7 Merge pull request #3028 from diegosouzapw/docs/mcp-tools-43
docs(mcp): regenerate mcp-tools diagram for 43 tools + fix count
2026-06-01 00:16:13 -03:00
diegosouzapw
74e290bc70 docs(mcp): regenerate mcp-tools diagram for 43 tools (+ notion); fix count in CLAUDE.md
release/v3.8.8 added notionTools.ts (6 tools) → MCP total is 43, not 37, but the
diagram asset (mcp-tools-43.svg) was never generated and MCP-SERVER.md/CLAUDE.md
still said 37. Create mcp-tools-43.{mmd,svg} (rendered via mermaid-cli), repoint
MCP-SERVER.md + docs/diagrams/README.md, update CLAUDE.md count to 43, and remove
the superseded mcp-tools-37 source/asset.

Note: full 'next build' OOM'd locally (session cgroup limit); the SVG ref resolves
(file present at path) and check:docs-sync passes — CI runs the authoritative build.
2026-06-01 00:15:20 -03:00
diegosouzapw
a0b435bcae chore: credit PRs 3000, 3006, 3008, 3010, 3012, 3015, 3018 in changelog 2026-05-31 22:19:27 -03:00
Diego Rodrigues de Sa e Souza
4b3987e190 Merge pull request #3022 from diegosouzapw/feat/quota-share-redesign
feat(quota): Pool Groups + correções de enforcement → v3.8.8 (+ authz peer-IP, screen fixes)
2026-05-31 22:05:18 -03:00
diegosouzapw
7f6e7a80e3 fix(docs): point MCP-SERVER diagram to existing mcp-tools-37.svg (merge unbroke build)
release/v3.8.8 referenced ../diagrams/exported/mcp-tools-43.svg (43 tools, after
notionTools added) but never generated the asset — only mcp-tools-37.svg exists,
so the merged tree failed 'next build' with Module not found. Point the image +
source back to the existing 37 asset with a note that the count of record is 43
(per the source-of-truth breakdown). Follow-up: regenerate mcp-tools-43.{mmd,svg}.
2026-05-31 21:21:14 -03:00
diegosouzapw
58a7d97b3f Merge branch 'release/v3.8.8' into feat/quota-share-redesign
# Conflicts:
#	src/lib/db/apiKeys.ts
#	src/lib/db/migrationRunner.ts
#	src/server/authz/policies/management.ts
#	src/server/authz/routeGuard.ts
#	tests/unit/route-guard-private-lan.test.ts
2026-05-31 21:08:30 -03:00
diegosouzapw
137d77cf12 feat(quota): group selector + grouped pool cards + wizard group pick (Interface A)
Task B9: QuotaSharePageClient fetches /api/quota/groups, renders a group bar
(select + New group + Rename group actions), and filters pool cards by the
selected group. PoolWizard adds a group picker in step 1 and POSTs groupId;
default pool name now uses provider slug instead of the raw connection
label/email. EditAllocationsModal adds a group allocation note. i18n parity
for 7 new quotaShare keys in en + pt-BR. PoolCreateSchema gains optional
groupId field. Test: tests/unit/quota-groups-ui.test.ts (21 source-scan
assertions, all green).
2026-05-31 19:52:00 -03:00
diegosouzapw
5d84a8fa7a test(quota): combo protection guard matches qtSd/ prefix 2026-05-31 19:33:24 -03:00
diegosouzapw
efa8f0af23 feat(quota): /api/quota/groups CRUD (rename re-syncs combos)
Add GET/POST /api/quota/groups and PATCH/DELETE /api/quota/groups/[id].
PATCH rename calls renameGroup then re-syncs quotaShared-* combos (via
dynamic-import syncQuotaCombos) for every pool in the group, since combo
names embed the group slug. DELETE maps the deleteGroup throw (protected
group-demo or pools still referencing the group) to 409 Conflict via
buildErrorBody — never 500. All handlers are management-gated via
requireManagementAuth and route all errors through buildErrorBody (Hard
Rule #12). 37-assertion source-scan test verifies auth, error sanitisation,
response shapes, Zod validation, PATCH re-sync wiring, and DELETE 409 logic.
2026-05-31 19:31:30 -03:00
diegosouzapw
eac24ca458 feat(quota): group-level allocations + group access enforcement
upsertAllocations now propagates allocation rows to every pool in the
same group. A key allocated to pool A in group G automatically gets
an allocation row in pool B (same group), so enforceQuotaShare finds
the row when the key calls pool B's model and applies fair-share.

No change to apiKeyPolicy Check 3 (already group-correct via B5
groupSlug logic) or enforce.ts pool-match (works once propagation
supplies the row).
2026-05-31 19:24:59 -03:00
Diego Rodrigues de Sa e Souza
9863000ab1 Merge PR 3019 into release/v3.8.8 2026-05-31 19:16:46 -03:00
diegosouzapw
e9cc53b17f feat(quota): key scope + /v1/models expand to the whole group (real group)
- `getPoolsByGroup(groupId)` in quotaPools.ts: SELECT all pools for a group,
  exposed as public API and re-exported from localDb.ts.
- `resolveQuotaKeyScope`: pool → group → all pools in group expansion.
  The returned `poolSlugs` field now holds GROUP slugs (quotaGroupSlug of the
  group name) instead of individual pool-name slugs, one per distinct group.
  Group slug included only when the group has ≥1 valid connection (group-level
  anyValidConnection gate). Deduplication: a key in 2 pools of the same group
  expands once.
- `filterModelsToQuotaPools` (quotaCombos.ts): already matches by
  `parsed.groupSlug` ∈ poolSlugs — no logic change needed, just test alignment.
- quota-key-resolve.test.ts: updated 4 tests that asserted pool-name slugs
  (testpoola2, poolmix, etc.) to assert the group slug ("groupdemo") — this is
  alignment to the B5 group semantics, not masking; the assertions were correct
  for the old pool-slug behavior and are now correct for the new group-slug
  behavior.
- quota-catalog-filter.test.ts: updated fixtures from old quotaShared-* format
  to qtSd/<groupSlug>/... (naming changed in B3, test was already broken before B5).
2026-05-31 19:15:16 -03:00