diff --git a/ui/src/components/BuiltInAgentGate.test.tsx b/ui/src/components/BuiltInAgentGate.test.tsx index 8617dd20cf..3c2d72fa47 100644 --- a/ui/src/components/BuiltInAgentGate.test.tsx +++ b/ui/src/components/BuiltInAgentGate.test.tsx @@ -10,6 +10,7 @@ import type { BuiltInAgentState, BuiltInAgentStatus } from "@/api/builtInAgents" const listMock = vi.hoisted(() => vi.fn()); const resumeMock = vi.hoisted(() => vi.fn()); +const getExperimentalMock = vi.hoisted(() => vi.fn()); vi.mock("@/api/builtInAgents", async (importOriginal) => { const actual = await importOriginal(); @@ -23,6 +24,10 @@ vi.mock("@/api/agents", () => ({ agentsApi: { resume: resumeMock }, })); +vi.mock("@/api/instanceSettings", () => ({ + instanceSettingsApi: { getExperimental: getExperimentalMock }, +})); + // The configure modal pulls in the full AgentConfigForm; stub it so the gate // test stays focused on state selection. vi.mock("@/components/ConfigureBuiltInAgentModal", () => ({ @@ -90,6 +95,8 @@ describe("BuiltInAgentGate (PAP-12978)", () => { document.body.appendChild(container); listMock.mockReset(); resumeMock.mockReset(); + getExperimentalMock.mockReset(); + getExperimentalMock.mockResolvedValue({ enableBuiltInAgents: true }); }); afterEach(() => { @@ -153,6 +160,16 @@ describe("BuiltInAgentGate (PAP-12978)", () => { expect(container.textContent).not.toContain("Set up the Briefs Agent"); }); + it("does not query built-in agents when the feature flag is off", async () => { + getExperimentalMock.mockResolvedValue({ enableBuiltInAgents: false }); + listMock.mockResolvedValue([makeState("ready")]); + + await renderGate(); + + expect(listMock).not.toHaveBeenCalled(); + expect(container.querySelector('[data-testid="feature"]')).not.toBeNull(); + }); + it("fails open to the feature when the key is unknown", async () => { listMock.mockResolvedValue([makeState("ready", { definition: { ...makeState("ready").definition, key: "learning" }, diff --git a/ui/src/components/BuiltInAgentGate.tsx b/ui/src/components/BuiltInAgentGate.tsx index 4d7cb8e4a0..93a75a090f 100644 --- a/ui/src/components/BuiltInAgentGate.tsx +++ b/ui/src/components/BuiltInAgentGate.tsx @@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button"; import { ConfigureBuiltInAgentModal } from "@/components/ConfigureBuiltInAgentModal"; import { builtInAgentsApi, type BuiltInAgentState } from "@/api/builtInAgents"; import { agentsApi } from "@/api/agents"; +import { instanceSettingsApi } from "@/api/instanceSettings"; import { queryKeys } from "@/lib/queryKeys"; import { agentUrl } from "@/lib/utils"; import { relativeTime } from "@/lib/utils"; @@ -36,10 +37,15 @@ export function BuiltInAgentGate({ agentKey, companyId, featureLabel, children } const queryClient = useQueryClient(); const [configureOpen, setConfigureOpen] = useState(false); - const { data: states, isLoading } = useQuery({ + const experimentalQuery = useQuery({ + queryKey: queryKeys.instance.experimentalSettings, + queryFn: () => instanceSettingsApi.getExperimental(), + }); + const builtInAgentsEnabled = experimentalQuery.data?.enableBuiltInAgents === true; + const { data: states, isLoading: statesLoading } = useQuery({ queryKey: queryKeys.builtInAgents.list(companyId ?? "__none__"), queryFn: () => builtInAgentsApi.list(companyId!), - enabled: Boolean(companyId), + enabled: Boolean(companyId && builtInAgentsEnabled), }); const state: BuiltInAgentState | undefined = states?.find((entry) => entry.definition.key === agentKey); @@ -54,7 +60,7 @@ export function BuiltInAgentGate({ agentKey, companyId, featureLabel, children } // Unknown key or still resolving the company — fail open to the feature. if (!companyId) return <>{children}; - if (isLoading && !states) return ; + if ((experimentalQuery.isLoading || statesLoading) && !states) return ; if (!state) return <>{children}; const label = featureLabel ?? state.definition.displayName; diff --git a/ui/src/components/SummarySlotCard.test.tsx b/ui/src/components/SummarySlotCard.test.tsx index e4276486ae..4e81c718ee 100644 --- a/ui/src/components/SummarySlotCard.test.tsx +++ b/ui/src/components/SummarySlotCard.test.tsx @@ -230,7 +230,10 @@ describe("SummarySlotCard", () => { beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); - mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableSummaries: true }); + mockInstanceSettingsApi.getExperimental.mockResolvedValue({ + enableSummaries: true, + enableBuiltInAgents: true, + }); mockBuiltInAgentsApi.list.mockResolvedValue([readySummarizer()]); mockSummarySlotsApi.get.mockResolvedValue({ slot: null, document: null, generatingIssue: null } satisfies GetSummarySlotResponse); mockSummarySlotsApi.revisions.mockResolvedValue({ slot: null, revisions: [] } satisfies ListSummarySlotRevisionsResponse); @@ -249,7 +252,10 @@ describe("SummarySlotCard", () => { }); it("renders nothing and does not fetch slots when the summaries flag is off", async () => { - mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableSummaries: false }); + mockInstanceSettingsApi.getExperimental.mockResolvedValue({ + enableSummaries: false, + enableBuiltInAgents: true, + }); root = renderCard(container); await flushQueries(); @@ -259,6 +265,18 @@ describe("SummarySlotCard", () => { expect(mockBuiltInAgentsApi.list).not.toHaveBeenCalled(); }); + it("does not query built-in agents when their feature flag is off", async () => { + mockInstanceSettingsApi.getExperimental.mockResolvedValue({ + enableSummaries: true, + enableBuiltInAgents: false, + }); + + root = renderCard(container); + await flushQueries(); + + expect(mockBuiltInAgentsApi.list).not.toHaveBeenCalled(); + }); + it("shows setup CTA when the Summarizer built-in agent needs setup", async () => { mockBuiltInAgentsApi.list.mockResolvedValue([needsSetupSummarizer()]); diff --git a/ui/src/components/SummarySlotCard.tsx b/ui/src/components/SummarySlotCard.tsx index 1a90e42705..9b94b0dfb3 100644 --- a/ui/src/components/SummarySlotCard.tsx +++ b/ui/src/components/SummarySlotCard.tsx @@ -154,11 +154,12 @@ export function SummarySlotCard({ queryFn: () => instanceSettingsApi.getExperimental(), }); const summariesEnabled = experimentalQuery.data?.enableSummaries === true; + const builtInAgentsEnabled = experimentalQuery.data?.enableBuiltInAgents === true; const builtInAgentsQuery = useQuery({ queryKey: queryKeys.builtInAgents.list(companyId ?? "__none__"), queryFn: () => builtInAgentsApi.list(companyId!), - enabled: Boolean(companyId && summariesEnabled), + enabled: Boolean(companyId && summariesEnabled && builtInAgentsEnabled), retry: false, });