From b103ff924b2eb01b9bf07398acdf6f3652d19454 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 4 Aug 2026 19:46:11 -0700 Subject: [PATCH 1/3] Remove Bifrost virtual key from the provider settings form Drop the Bifrost toggle from agent-modal so operators no longer configure x-bf-vk through the UI. Existing runtime handling for stored configs is left intact. Closes CL-5344 --- src/tui/components/agent-modal.test.ts | 3 +-- src/tui/components/agent-modal.tsx | 19 +++---------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/tui/components/agent-modal.test.ts b/src/tui/components/agent-modal.test.ts index a25ed4058..c503c092a 100644 --- a/src/tui/components/agent-modal.test.ts +++ b/src/tui/components/agent-modal.test.ts @@ -26,9 +26,8 @@ const form = (overrides: Partial = {}): ProviderFormValues = keyless: "no", models: "fp-large, fp-small", defaultModel: "fp-large", - bifrostVirtualKey: "no", ...overrides, -} as ProviderFormValues); +}); describe("validateProviderForm", () => { test("creates a provider submission from comma-separated models", () => { diff --git a/src/tui/components/agent-modal.tsx b/src/tui/components/agent-modal.tsx index 8e5860810..9ed0f0632 100644 --- a/src/tui/components/agent-modal.tsx +++ b/src/tui/components/agent-modal.tsx @@ -48,7 +48,7 @@ export type AgentProvider = { export type { ProviderSubmission, ProviderSubmission as ProviderFormSubmission }; -export type ProviderFormField = "name" | "baseURL" | "keyless" | "apiKey" | "models" | "defaultModel" | "bifrostVirtualKey"; +export type ProviderFormField = "name" | "baseURL" | "keyless" | "apiKey" | "models" | "defaultModel"; export type ProviderFormValues = Record; type Step = | "provider" @@ -62,7 +62,7 @@ type Step = | "profile-form" | "profile-delete"; -const FORM_FIELDS: readonly ProviderFormField[] = ["name", "baseURL", "keyless", "apiKey", "models", "defaultModel", "bifrostVirtualKey"]; +const FORM_FIELDS: readonly ProviderFormField[] = ["name", "baseURL", "keyless", "apiKey", "models", "defaultModel"]; const FIELD_LABELS: Record = { name: "Provider name", @@ -71,7 +71,6 @@ const FIELD_LABELS: Record = { apiKey: "API key", models: "Models", defaultModel: "Default model", - bifrostVirtualKey: "Bifrost virtual key", }; const FIELD_HINTS: Record = { @@ -81,7 +80,6 @@ const FIELD_HINTS: Record = { apiKey: "sk-...", models: "model-a, model-b", defaultModel: "optional; must be in models", - bifrostVirtualKey: "yes = use x-bf-vk header (Bifrost)", }; // Project provider catalog entries carry credentials. The modal receives the @@ -153,7 +151,6 @@ function initialFormValues(provider: AgentProvider | undefined): ProviderFormVal apiKey: "", models: provider?.models.join(", ") ?? "", defaultModel: provider?.defaultModel ?? provider?.models[0] ?? "", - bifrostVirtualKey: provider?.bifrostVirtualKey === true ? "yes" : "no", }; } @@ -172,7 +169,6 @@ export function validateProviderForm( const baseURL = values.baseURL.trim(); const apiKey = values.apiKey.trim(); const keyless = values.keyless === "yes"; - const bifrostVirtualKey = values.bifrostVirtualKey === "yes"; const models = parseModels(values.models); const defaultModel = values.defaultModel.trim(); @@ -196,7 +192,6 @@ export function validateProviderForm( ...(apiKey.length > 0 ? { apiKey } : {}), models, ...(defaultModel.length > 0 ? { defaultModel } : {}), - ...(bifrostVirtualKey ? { bifrostVirtualKey: true } : {}), }, }; } @@ -768,13 +763,6 @@ export function AgentModal({ } return; } - if (currentField === "bifrostVirtualKey") { - if (key.leftArrow || key.rightArrow || input === " ") { - setFormValues((v) => ({ ...v, bifrostVirtualKey: v.bifrostVirtualKey === "yes" ? "no" : "yes" })); - setFormError(null); - } - return; - } if (key.backspace || key.delete) { setFormValues((values) => ({ ...values, [currentField]: values[currentField].slice(0, -1) })); setFormError(null); @@ -966,7 +954,6 @@ export function AgentModal({ const showCaret = isCursor && field !== "keyless" && - field !== "bifrostVirtualKey" && !(field === "apiKey" && isKeyless); return ( @@ -975,7 +962,7 @@ export function AgentModal({ {FIELD_LABELS[field]} - {field === "keyless" || field === "bifrostVirtualKey" ? ( + {field === "keyless" ? ( {isCursor ? "< " : " "} {value === "yes" ? "yes" : "no"} From cc67ed7e90282b912b1cab8adfe64f133b003ed6 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 4 Aug 2026 20:11:38 -0700 Subject: [PATCH 2/3] Preserve bifrostVirtualKey when re-saving a provider without the form field The settings form no longer exposes Bifrost, but re-editing a provider must not drop a previously stored virtual-key flag used for x-bf-vk routing. --- src/config/providers.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/providers.ts b/src/config/providers.ts index 884c86ff6..f7243f20b 100644 --- a/src/config/providers.ts +++ b/src/config/providers.ts @@ -41,7 +41,11 @@ export function buildProviderEntry( ...(apiKey !== undefined && apiKey.length > 0 ? { apiKey } : {}), models: submission.models, ...(submission.defaultModel !== undefined ? { defaultModel: submission.defaultModel } : {}), - ...(submission.bifrostVirtualKey === true ? { bifrostVirtualKey: true } : {}), + // Form no longer exposes Bifrost; keep any previously stored flag on edit so + // re-saving a provider does not silently drop x-bf-vk routing. + ...(submission.bifrostVirtualKey === true || existing?.bifrostVirtualKey === true + ? { bifrostVirtualKey: true } + : {}), }; const catalog = currentCatalog .filter((p) => p.name !== submission.name && p.name !== submission.originalName) From e6db3bf0927bb2252e765f89ddeacb013d28c52e Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 4 Aug 2026 22:37:18 -0700 Subject: [PATCH 3/3] Add unit test for bifrostVirtualKey preserve-on-edit --- src/config/providers.test.ts | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/config/providers.test.ts diff --git a/src/config/providers.test.ts b/src/config/providers.test.ts new file mode 100644 index 000000000..bca35737b --- /dev/null +++ b/src/config/providers.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, test } from "bun:test"; + +import type { ProviderCatalogEntry } from "./index.js"; +import { buildProviderEntry } from "./providers.js"; + +const baseCatalog: ProviderCatalogEntry[] = [ + { + name: "bf", + baseURL: "http://localhost:8080/v1", + apiKey: "sk-bf-existing", + models: ["m1"], + bifrostVirtualKey: true, + }, + { + name: "openai", + baseURL: "https://api.openai.com/v1", + apiKey: "sk-openai", + models: ["gpt-4o"], + }, +]; + +describe("buildProviderEntry bifrostVirtualKey preserve-on-edit", () => { + test("keeps existing bifrostVirtualKey when submission omits the flag", () => { + const result = buildProviderEntry( + { + name: "bf", + originalName: "bf", + baseURL: "http://localhost:8080/v1", + models: ["m1", "m2"], + }, + baseCatalog, + ); + + expect(result.ok).toBe(true); + if (!result.ok) return; + expect(result.entry.bifrostVirtualKey).toBe(true); + }); + + test("omits bifrostVirtualKey when existing entry has no flag", () => { + const result = buildProviderEntry( + { + name: "openai", + originalName: "openai", + baseURL: "https://api.openai.com/v1", + models: ["gpt-4o-mini"], + }, + baseCatalog, + ); + + expect(result.ok).toBe(true); + if (!result.ok) return; + expect(result.entry.bifrostVirtualKey).toBeUndefined(); + }); +});