Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

### 🐛 Bug Fixes

- **fix(dashboard):** adding a second API key connection for the same provider no longer silently overwrites the first — the Add-API-key modal now derives a unique default connection name (`main`, then `main-2`, `main-3`, …) so the backend name-based upsert can't collide (#6499 — thanks @dilneiss).
- **fix(compression):** the session-dedup engine now also deduplicates a large multi-line block repeated **within a single message** (intra-message dedup), not just across turns; the compression-preview API surfaces a `fallbackReason`, and the fusion panel reports how many models were rate-limited vs failed on total-panel failure (#6501 — thanks @chirag127).
- **fix(compression):** a stacked-pipeline step naming an unregistered engine now surfaces a `validationErrors` entry instead of silently no-op'ing, so misconfigured pipelines are visible in the preview API (#6506 — thanks @chirag127).
- **feat(usage):** add a **Codex reset-credit redemption** flow to the Provider Limits UI — a `useCodexResetCreditRedemption` hook + `/api/usage/codex-reset-credit` route + `codexResetCredits` lib let you redeem banked Codex reset credits from the quota card. Regression guard: `tests/unit/codex-reset-credits.test.ts`. (thanks @JxnLexn)
Expand Down
5 changes: 3 additions & 2 deletions config/quality/file-size-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@
"src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx": 847,
"src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 784,
"src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx": 942,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 952,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 959,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1278,
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 954,
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderModels.ts": 155,
Expand Down Expand Up @@ -378,5 +378,6 @@
"_rebaseline_2026_07_05_6235_doubao_dola": "PR #6235 own growth: tests/unit/web-cookie-providers-new.test.ts 850->890 (+40 = doubao-web -> Dola global provider switch regression cases: new host/cookie-domain/token-source assertions for www.dola.com). Cohesive test growth alongside the provider switch; contributor backryun. Fast-path PR->release skips check:file-size, so this bump lands with the PR.",
"_rebaseline_2026_07_06_v3845_release_close": "Release v3.8.45 cycle-close rebaseline (captain, sess ce897453): 13 files grown by the cycle's merged fix/feature PRs (#6216 streaming fixes + request-logger UI grew RequestLoggerV2/chat/chatHelpers/auth/stream/response-sanitizer.test; #6251/#6253 dashboard UX grew combos page/modals/wizard/ComboDefaultsTab/ProxyRegistryManager/providerPageHelpers). Growth is legitimate merged-feature code, absorbed at release per Phase 0 drift policy; all remain frozen (cannot grow further).",
"_rebaseline_2026_07_06_6118_zed_oauthmodal": "PR #6118 own growth: OAuthModal.tsx 989->993 (+4 = Zed hosted native-app sign-in modal branch). Cohesive UI growth for the zed-hosted OAuth provider; not extractable. The prior 6118 comment set the note but left the frozen value at 989.",
"_rebaseline_2026_07_06_6351_glm_team_quota": "PR #6351 own growth (GLM team-plan quota fields threaded through the connection modals; new GlmTeamQuotaFields.tsx extracted): AddApiKeyModal.tsx ->951 (+9), EditConnectionModal.tsx ->1277 (+18). Absorbs the pre-existing session base-red on these frozen modals; release captain rebaseline-at-release supersedes."
"_rebaseline_2026_07_06_6351_glm_team_quota": "PR #6351 own growth (GLM team-plan quota fields threaded through the connection modals; new GlmTeamQuotaFields.tsx extracted): AddApiKeyModal.tsx ->951 (+9), EditConnectionModal.tsx ->1277 (+18). Absorbs the pre-existing session base-red on these frozen modals; release captain rebaseline-at-release supersedes.",
"_rebaseline_2026_07_06_6499_unique_default_name": "PR #6499 own growth: AddApiKeyModal.tsx 952->959 (+7 = a unique default connection name so a second API key for the same provider does not reuse 'main' and trigger the backend name-based upsert that silently overwrote the first connection). The pure name derivation was extracted to computeConnectionDefaultName.ts (unit-tested) to keep the growth minimal; the contributor's original full-form-reset rewrite was trimmed to a spread reset to avoid dropping the GLM team-quota fields #6351 added and to hold the frozen god-file growth down. Release captain rebaseline-at-release supersedes."
}
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@ export default function ProviderDetailPageClient() {
isCommandCode={isCommandCode}
isUpstreamProxyProvider={isUpstreamProxyProvider}
subscriptionRisk={subscriptionRisk}
existingConnectionCount={connections.length}
showRiskNoticeModal={showRiskNoticeModal}
handleConfirmRiskNotice={handleConfirmRiskNotice}
handleCancelRiskNotice={handleCancelRiskNotice}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ interface ProviderModalsPanelProps {
isCommandCode: boolean;
isUpstreamProxyProvider: boolean;
subscriptionRisk: boolean;
existingConnectionCount?: number;
// Risk notice
showRiskNoticeModal: boolean;
handleConfirmRiskNotice: () => void;
Expand Down Expand Up @@ -146,6 +147,7 @@ export default function ProviderModalsPanel({
isCcCompatible,
isUpstreamProxyProvider,
subscriptionRisk,
existingConnectionCount,
showRiskNoticeModal,
handleConfirmRiskNotice,
handleCancelRiskNotice,
Expand Down Expand Up @@ -280,6 +282,7 @@ export default function ProviderModalsPanel({
providerName={providerInfo.name}
providerWebsite={providerInfo.website}
initialBaseUrl={siliconFlowInitialBaseUrl}
existingConnectionCount={existingConnectionCount}
isCompatible={isCompatible}
isAnthropic={isAnthropicProtocolCompatible}
isCcCompatible={isCcCompatible}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useOpenRouterPresetControl } from "../OpenRouterPresetInput";
import WebSessionCredentialGuide from "../WebSessionCredentialGuide";
import CcCompatibleRequestDefaultsFields from "./CcCompatibleRequestDefaultsFields";
import { buildAddProviderSpecificData } from "./connectionProviderSpecificData";
import { computeConnectionDefaultName } from "./computeConnectionDefaultName";
import QuotaScrapingFields, { EMPTY_QUOTA_SCRAPING_FIELDS } from "./QuotaScrapingFields";
import GlmTeamQuotaFields, { EMPTY_GLM_TEAM_QUOTA_FIELDS } from "./GlmTeamQuotaFields";
export interface AddApiKeyModalProps {
Expand All @@ -41,6 +42,7 @@ export interface AddApiKeyModalProps {
providerName?: string;
providerWebsite?: string;
initialBaseUrl?: string;
existingConnectionCount?: number;
isCompatible?: boolean;
isAnthropic?: boolean;
isCcCompatible?: boolean;
Expand All @@ -64,6 +66,7 @@ export default function AddApiKeyModal({
providerName,
providerWebsite,
initialBaseUrl,
existingConnectionCount = 0,
isCompatible,
isAnthropic,
isCcCompatible,
Expand Down Expand Up @@ -114,7 +117,7 @@ export default function AddApiKeyModal({
}[commandCodeAuthState.phase]
: null;
const [formData, setFormData] = useState({
name: "main", // #5421: required field; default resists autofill garbage (was "" → "wiw")
name: computeConnectionDefaultName(existingConnectionCount),
apiKey: "",
tokenSecret: "", // #5446 — Modal Token Secret (joined with apiKey as id:secret)
defaultModel: "",
Expand Down Expand Up @@ -148,11 +151,15 @@ export default function AddApiKeyModal({
const wasOpen = wasOpenRef.current;
wasOpenRef.current = isOpen;
if (!isOpen || wasOpen) return;
// On open, reset baseUrl and assign a unique default name so a second API key
// for the same provider doesn't reuse "main" and trigger the backend
// name-based upsert that would silently overwrite the first connection (#6499).
setFormData((current) => ({
...current,
name: computeConnectionDefaultName(existingConnectionCount),
baseUrl: initialBaseUrl || defaultBaseUrl,
}));
}, [defaultBaseUrl, initialBaseUrl, isOpen]);
}, [defaultBaseUrl, initialBaseUrl, isOpen, existingConnectionCount]);
const bulkSupported = supportsBulkApiKey(provider);
const [mode, setMode] = useState<"single" | "bulk">("single");
const [bulkText, setBulkText] = useState("");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// #6499 — a fresh API-key connection defaults its name to "main". The backend
// upserts connections by (provider, name), so opening the modal a second time for
// the same provider with the same "main" name silently OVERWRITES the first
// connection. Deriving a unique default from the existing connection count keeps
// the first connection ("main") backward-compatible while giving each subsequent
// one a distinct name ("main-2", "main-3", …).
export function computeConnectionDefaultName(existingConnectionCount?: number): string {
const count = existingConnectionCount ?? 0;
return count <= 0 ? "main" : `main-${count + 1}`;
}
27 changes: 27 additions & 0 deletions tests/unit/compute-connection-default-name-6499.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { test } from "node:test";
import assert from "node:assert/strict";

import { computeConnectionDefaultName } from "@/app/(dashboard)/dashboard/providers/[id]/components/modals/computeConnectionDefaultName";

// #6499 — the backend upserts API-key connections by (provider, name), so a second
// connection defaulting to "main" silently overwrites the first. The default name
// must stay "main" for the first connection (backward compatible) and get a unique
// numeric suffix afterwards.

test("first connection defaults to 'main'", () => {
assert.equal(computeConnectionDefaultName(0), "main");
});

test("undefined count is treated as zero → 'main'", () => {
assert.equal(computeConnectionDefaultName(undefined), "main");
});

test("subsequent connections get a unique numeric suffix", () => {
assert.equal(computeConnectionDefaultName(1), "main-2");
assert.equal(computeConnectionDefaultName(2), "main-3");
assert.equal(computeConnectionDefaultName(9), "main-10");
});

test("negative counts are clamped to 'main' (never a broken 'main-0')", () => {
assert.equal(computeConnectionDefaultName(-1), "main");
});