diff --git a/desktop/src/features/agents/AGENTS.md b/desktop/src/features/agents/AGENTS.md index 146ff34e8..bae3389d5 100644 --- a/desktop/src/features/agents/AGENTS.md +++ b/desktop/src/features/agents/AGENTS.md @@ -93,7 +93,9 @@ with a TypeScript lookup table or an id comparison in a component. runtime off must not uninstall it, stop it, or invalidate an existing agent that already uses it. If the disabled runtime was the saved global default, consumers immediately ignore that preference and the defaults - editor persists its visible fallback on the next save. + editor persists its visible fallback on the next save. Runtime-less agent + starts and team deploys must filter through the same visible-runtime set; + definitions already pinned to a hidden runtime remain runnable. 10. **The defaults modal is progressively disclosed.** An unset global config starts on the Buzz Agent-first deployment fallback and carries that visible harness into the next saved edit. The `progressive-defaults` disclosure diff --git a/desktop/src/features/agents/lib/runtimeVisibilityPreference.test.mjs b/desktop/src/features/agents/lib/runtimeVisibilityPreference.test.mjs index fcf1755ea..629f28559 100644 --- a/desktop/src/features/agents/lib/runtimeVisibilityPreference.test.mjs +++ b/desktop/src/features/agents/lib/runtimeVisibilityPreference.test.mjs @@ -8,6 +8,7 @@ import { nextDisabledAcpRuntimeIds, parseDisabledAcpRuntimeIds, readDisabledAcpRuntimeIds, + runtimesForImplicitAcpSelection, } from "./runtimeVisibilityPreference.ts"; test("runtime visibility parsing is normalized and corruption tolerant", () => { @@ -40,6 +41,18 @@ test("disabled runtimes are removed from selectable catalog entries", () => { runtimes[0], runtimes[2], ]); + assert.deepEqual(filterEnabledAcpRuntimes(runtimes, ["buzz-agent"]), [ + runtimes[1], + runtimes[2], + ]); + assert.deepEqual( + runtimesForImplicitAcpSelection(runtimes, ["buzz-agent"], null), + [runtimes[1], runtimes[2]], + ); + assert.deepEqual( + runtimesForImplicitAcpSelection(runtimes, ["buzz-agent"], "buzz-agent"), + runtimes, + ); }); test("stored runtime visibility is read from the versioned device key", () => { diff --git a/desktop/src/features/agents/lib/runtimeVisibilityPreference.ts b/desktop/src/features/agents/lib/runtimeVisibilityPreference.ts index c364c8b0f..dcda74a41 100644 --- a/desktop/src/features/agents/lib/runtimeVisibilityPreference.ts +++ b/desktop/src/features/agents/lib/runtimeVisibilityPreference.ts @@ -95,6 +95,20 @@ export function filterEnabledAcpRuntimes( ); } +/** + * Apply visibility only when the app is choosing a runtime implicitly. + * Existing definitions pinned to a runtime remain runnable. + */ +export function runtimesForImplicitAcpSelection( + runtimes: readonly T[], + disabledRuntimeIds: readonly string[], + explicitRuntimeId?: string | null, +): T[] { + return explicitRuntimeId?.trim() + ? [...runtimes] + : filterEnabledAcpRuntimes(runtimes, disabledRuntimeIds); +} + /** * Prevent a disabled runtime from remaining the effective global preference. * diff --git a/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx b/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx index 99d3167d0..8919666a3 100644 --- a/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx +++ b/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx @@ -11,6 +11,7 @@ import { emptyResolvedTeamPersonas, resolveTeamPersonas, } from "@/features/agents/lib/teamPersonas"; +import { useSelectableAcpRuntimes } from "@/features/agents/lib/runtimeVisibilityPreference"; import { collectRuntimeWarnings, getDefaultPersonaRuntime, @@ -68,11 +69,12 @@ export function AddTeamToChannelDialog({ [channelsQuery.data], ); - const runtimes = providersQuery.data ?? []; + const runtimes = providersQuery.data; + const selectableRuntimes = useSelectableAcpRuntimes(runtimes); // Use the buzz-agent-first preference so the team-deploy fallback mirrors the // single-agent start path (buzz-agent → goose → first available). const defaultProvider = getDefaultPersonaRuntime( - runtimes, + selectableRuntimes, globalConfig.preferred_runtime, ); diff --git a/desktop/src/features/agents/ui/useManagedAgentActions.ts b/desktop/src/features/agents/ui/useManagedAgentActions.ts index e1c2e9c9f..b4f216dc8 100644 --- a/desktop/src/features/agents/ui/useManagedAgentActions.ts +++ b/desktop/src/features/agents/ui/useManagedAgentActions.ts @@ -35,9 +35,14 @@ import { buildInstanceInputForDefinition, resolveStartRuntimeForDefinition, } from "../lib/instanceInputForDefinition"; +import { + runtimesForImplicitAcpSelection, + useDisabledAcpRuntimeIds, +} from "../lib/runtimeVisibilityPreference"; export function useManagedAgentActions() { const { globalConfig } = useGlobalAgentConfig(); + const disabledRuntimeIds = useDisabledAcpRuntimeIds(); const relayAgentsQuery = useRelayAgentsQuery(); const managedAgentsQuery = useManagedAgentsQuery(); const [shouldLoadChannels, setShouldLoadChannels] = React.useState(false); @@ -192,7 +197,11 @@ export function useManagedAgentActions() { setPersonaStartPending(persona.id, true); clearFeedback(); try { - const runtimes = await availableRuntimesForStart(availableRuntimesQuery); + const runtimes = runtimesForImplicitAcpSelection( + await availableRuntimesForStart(availableRuntimesQuery), + disabledRuntimeIds, + persona.runtime, + ); const { runtime, warnings } = resolveStartRuntimeForDefinition( persona, runtimes,