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.
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.
* 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>
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.
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>
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>
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!
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!
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!
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!
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!
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!
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!
- 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)
- 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
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).
- 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)
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.
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}.
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).
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.
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).
- `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).