diff --git a/desktop/src/features/agents/lib/resolvePersonaRuntime.test.mjs b/desktop/src/features/agents/lib/resolvePersonaRuntime.test.mjs index 6a5b66a5e..89991a705 100644 --- a/desktop/src/features/agents/lib/resolvePersonaRuntime.test.mjs +++ b/desktop/src/features/agents/lib/resolvePersonaRuntime.test.mjs @@ -8,9 +8,10 @@ import { } from "./resolvePersonaRuntime.ts"; function makeRuntime(id, label = `${id} label`) { - return { id, label, command: id, avatarUrl: "" }; + return { id, label, command: id, avatarUrl: "", availability: "available" }; } +const buzzAgent = makeRuntime("buzz-agent", "Buzz Agent"); const goose = makeRuntime("goose", "Goose"); const claude = makeRuntime("claude", "Claude"); const runtimes = [goose, claude]; @@ -42,6 +43,17 @@ test("resolvePersonaRuntime — hidden defaults are skipped for runtime-less per }); }); +test("resolvePersonaRuntime — hidden defaults preserve product fallback order", () => { + const result = resolvePersonaRuntime( + null, + [goose, claude, buzzAgent], + goose, + false, + ["goose"], + ); + assert.equal(result.runtime, buzzAgent); +}); + test("resolvePersonaRuntime — explicitly pinned hidden runtimes remain available", () => { const result = resolvePersonaRuntime("goose", runtimes, claude, false, [ "goose", diff --git a/desktop/src/features/agents/lib/resolvePersonaRuntime.ts b/desktop/src/features/agents/lib/resolvePersonaRuntime.ts index 3b09e5b4f..69f484d1e 100644 --- a/desktop/src/features/agents/lib/resolvePersonaRuntime.ts +++ b/desktop/src/features/agents/lib/resolvePersonaRuntime.ts @@ -149,11 +149,7 @@ function resolveVisibleDefaultRuntime( runtimes, disabledRuntimeIds, ); - return ( - visibleRuntimes.find((runtime) => runtime.id === defaultRuntime.id) ?? - visibleRuntimes[0] ?? - null - ); + return getDefaultPersonaRuntime(visibleRuntimes, defaultRuntime.id); } /** diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx index 4557de3d7..bdad2f01c 100644 --- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx +++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx @@ -35,11 +35,11 @@ import { import { AUTO_MODEL_DROPDOWN_VALUE, AUTO_PROVIDER_DROPDOWN_VALUE, - BLOCK_BUILD_HIDDEN_PROVIDER_IDS, CUSTOM_PROVIDER_DROPDOWN_VALUE, computeLocalModeGate, formatRuntimeOptionLabel, getDefaultPersonaRuntime, + getPersonaHiddenProviderIds, getPersonaModelOptions, getPersonaProviderOptions, getRuntimePersonaModelOptions, @@ -528,17 +528,12 @@ export function AgentDefinitionDialog({ modelFieldVisible, provider: effectiveProvider, }); - // On internal Block builds, BUZZ_AGENT_PROVIDER is baked in and a boot - // migration rewrites any persisted Databricks v1 values → v2. Hide the v1 - // option there so it is not offered for new selections. OSS builds have no - // baked provider, so v1 remains visible. - const hideProviderIds = React.useMemo( - () => - (bakedEnvKeys ?? []).includes("BUZZ_AGENT_PROVIDER") - ? BLOCK_BUILD_HIDDEN_PROVIDER_IDS - : new Set(), - [bakedEnvKeys], - ); + const hideProviderIds = getPersonaHiddenProviderIds({ + bakedEnvKeys: bakedEnvKeys ?? [], + selectableRuntimes, + currentRuntimeId: runtime, + preserveCurrentRuntime: !isCreateMode, + }); const providerOptions = getPersonaProviderOptions( trimmedProvider, runtime, diff --git a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx index 1f8e140f4..21116d05a 100644 --- a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx +++ b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx @@ -27,12 +27,13 @@ import { setManagedAgentAutoRestart } from "@/shared/api/tauriManagedAgents"; import { EditAgentAdvancedFields } from "./EditAgentAdvancedFields"; import { AUTO_PROVIDER_DROPDOWN_VALUE, - BLOCK_BUILD_HIDDEN_PROVIDER_IDS, CUSTOM_PROVIDER_DROPDOWN_VALUE, formatRuntimeOptionLabel, getDefaultLlmModelLabel, getDefaultPersonaRuntime, + getPersonaHiddenProviderIds, getPersonaProviderOptions, + getProviderApiKeyEnvVar, isMissingRequiredDropdownField, NO_RUNTIME_DROPDOWN_VALUE, PERSONA_FIELD_CONTROL_CLASS, @@ -75,7 +76,6 @@ import { getBakedModelInheritLabel, getBakedProviderInheritLabel, } from "./bakedEnvHelpers"; -import { getProviderApiKeyEnvVar } from "./agentConfigOptions"; import { useAgentDialogDefaults } from "./useAgentDialogDefaults"; import { AgentAiDefaultsNotice } from "./AgentAiDefaults"; import { AgentDefaultsDialog } from "./AgentDefaultsDialog"; @@ -790,13 +790,12 @@ export function AgentInstanceEditDialog({ // Provider field derived state const trimmedProvider = provider.trim(); - const hideProviderIds = React.useMemo( - () => - (bakedEnvKeys ?? []).includes("BUZZ_AGENT_PROVIDER") - ? BLOCK_BUILD_HIDDEN_PROVIDER_IDS - : new Set(), - [bakedEnvKeys], - ); + const hideProviderIds = getPersonaHiddenProviderIds({ + bakedEnvKeys: bakedEnvKeys ?? [], + selectableRuntimes, + currentRuntimeId: selectedRuntimeId, + preserveCurrentRuntime: true, + }); const providerOptions = getPersonaProviderOptions( trimmedProvider, selectedRuntime?.id ?? "", diff --git a/desktop/src/features/agents/ui/agentConfigOptions.test.mjs b/desktop/src/features/agents/ui/agentConfigOptions.test.mjs index ed71f6477..ae078f2fc 100644 --- a/desktop/src/features/agents/ui/agentConfigOptions.test.mjs +++ b/desktop/src/features/agents/ui/agentConfigOptions.test.mjs @@ -3,6 +3,7 @@ import test from "node:test"; import { getDefaultPersonaRuntime, + getPersonaHiddenProviderIds, getPersonaModelOptions, getPersonaProviderOptions, reconcilePreferredRuntimeFallback, @@ -75,6 +76,32 @@ test("getPersonaProviderOptions appends (current) tail for an unknown saved prov assert.equal(tail?.label, "my-custom-llm (current)"); }); +test("hidden Buzz Agent suppresses shared compute for new selections", () => { + const hidden = getPersonaHiddenProviderIds({ + bakedEnvKeys: [], + selectableRuntimes: [makeRuntime("goose")], + currentRuntimeId: "goose", + preserveCurrentRuntime: false, + }); + const ids = getPersonaProviderOptions("", "goose", "", hidden).map( + (option) => option.id, + ); + assert.ok(!ids.includes("relay-mesh")); +}); + +test("an existing hidden Buzz Agent keeps its shared compute provider", () => { + const hidden = getPersonaHiddenProviderIds({ + bakedEnvKeys: [], + selectableRuntimes: [makeRuntime("goose")], + currentRuntimeId: "buzz-agent", + preserveCurrentRuntime: true, + }); + const ids = getPersonaProviderOptions("", "buzz-agent", "", hidden).map( + (option) => option.id, + ); + assert.ok(ids.includes("relay-mesh")); +}); + // ── getDefaultPersonaRuntime — buzz-agent first ─────────────────────────────── test("getDefaultPersonaRuntime honors an available global preference", () => { diff --git a/desktop/src/features/agents/ui/agentConfigOptions.tsx b/desktop/src/features/agents/ui/agentConfigOptions.tsx index 2eecb3671..fda3825e1 100644 --- a/desktop/src/features/agents/ui/agentConfigOptions.tsx +++ b/desktop/src/features/agents/ui/agentConfigOptions.tsx @@ -14,15 +14,34 @@ export { getDefaultPersonaRuntime } from "../lib/resolvePersonaRuntime"; * offering it for new selections would create a regression path. * OSS builds pass an empty `Set` so v1 remains visible. * - * All three dialog sites that show a provider picker import this constant — - * `AgentDefinitionDialog`, `AgentInstanceEditDialog`, and - * `AgentDefaultsSettingsCard` — making it the single source of truth for - * which provider ids to suppress on Block builds. + * Provider pickers consume this directly or through + * `getPersonaHiddenProviderIds`, keeping one source of truth for Block builds. */ export const BLOCK_BUILD_HIDDEN_PROVIDER_IDS: ReadonlySet = new Set([ "databricks", ]); +export function getPersonaHiddenProviderIds({ + bakedEnvKeys, + selectableRuntimes, + currentRuntimeId, + preserveCurrentRuntime, +}: { + bakedEnvKeys: readonly string[]; + selectableRuntimes: readonly Pick[]; + currentRuntimeId: string; + preserveCurrentRuntime: boolean; +}): ReadonlySet { + const hidden = bakedEnvKeys.includes("BUZZ_AGENT_PROVIDER") + ? new Set(BLOCK_BUILD_HIDDEN_PROVIDER_IDS) + : new Set(); + const buzzAgentSelectable = + selectableRuntimes.some((runtime) => runtime.id === "buzz-agent") || + (preserveCurrentRuntime && currentRuntimeId.trim() === "buzz-agent"); + if (!buzzAgentSelectable) hidden.add("relay-mesh"); + return hidden; +} + export const PERSONA_FIELD_SHELL_CLASS = "rounded-xl border border-input bg-muted/40 transition-colors duration-150 ease-out hover:border-muted-foreground/40 focus-within:border-muted-foreground/50"; export const PERSONA_FIELD_CONTROL_CLASS =