From aff6b7e21296f4b6b97883ce8302b640992ec0b1 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 13:51:04 -0700 Subject: [PATCH] fix(ui): clear agents when updating team permissions (#39600) Always serialize object_permission.agents and agent_access_groups in the team update payload so removing the last agent in the dashboard sends an explicit empty array instead of omitting the key, which the backend merge treats as no change Resolves LIT-6861 Co-authored-by: yassin --- .../src/components/team/TeamInfo.test.tsx | 45 +++++++++++++++++++ .../src/components/team/TeamInfo.tsx | 8 +--- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx index a9c1077e96b..aedc04283f5 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx @@ -1888,6 +1888,8 @@ describe("TeamInfoView - the exact bytes the update call sends", () => { mcp_access_groups: [], mcp_tool_permissions: {}, mcp_toolsets: [], + agents: [], + agent_access_groups: [], vector_stores: ["vs-1"], }; @@ -1908,6 +1910,49 @@ describe("TeamInfoView - the exact bytes the update call sends", () => { }); }); + const openEditorWithAgents = async (user: ReturnType) => { + vi.mocked(networking.teamInfoCall).mockResolvedValue( + createMockTeamData({ + models: ["gpt-4"], + object_permission: { agents: ["agent-1"], agent_access_groups: ["group-a"] }, + }), + ); + vi.mocked(networking.teamUpdateCall).mockResolvedValue({ data: {}, team_id: "123" } as any); + + renderWithProviders(); + await waitFor(() => expect(screen.queryAllByText("Test Team").length).toBeGreaterThan(0)); + await user.click(screen.getByRole("tab", { name: "Settings" })); + await user.click(await screen.findByRole("button", { name: /edit settings/i })); + await screen.findByLabelText("Team Name"); + }; + + it("resends the stored agents and agent_access_groups when the selector is left untouched", async () => { + const user = userEvent.setup({ delay: null }); + await openEditorWithAgents(user); + + const payload = await save(user); + + const objectPermission = wireBody(payload).object_permission as Record; + expect(objectPermission.agents).toStrictEqual(["agent-1"]); + expect(objectPermission.agent_access_groups).toStrictEqual(["group-a"]); + }); + + it("sends empty agents and agent_access_groups arrays after the last agent chip is removed", async () => { + const user = userEvent.setup({ delay: null }); + await openEditorWithAgents(user); + + await user.click(within(screen.getByLabelText("agent-1")).getByRole("button")); + await user.click(within(screen.getByLabelText("group:group-a")).getByRole("button")); + expect(screen.queryByLabelText("agent-1")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("group:group-a")).not.toBeInTheDocument(); + + const payload = await save(user); + + const objectPermission = wireBody(payload).object_permission as Record; + expect(objectPermission.agents).toStrictEqual([]); + expect(objectPermission.agent_access_groups).toStrictEqual([]); + }); + it("resends every stored value once both sections are opened", async () => { const user = userEvent.setup({ delay: null }); await openEditor(user); diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index 3f6d6a96972..c2b8cd3cc56 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -863,12 +863,8 @@ const TeamInfoView: React.FC = ({ agents: [], accessGroups: [], }; - if (agents && agents.length > 0) { - updateData.object_permission.agents = agents; - } - if (agentAccessGroups && agentAccessGroups.length > 0) { - updateData.object_permission.agent_access_groups = agentAccessGroups; - } + updateData.object_permission.agents = agents; + updateData.object_permission.agent_access_groups = agentAccessGroups; delete values.agents_and_groups; // Handle vector stores permissions