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 <yassin@berri.ai>
This commit is contained in:
devin-ai-integration[bot] 2026-09-03 13:51:04 -07:00 committed by GitHub
parent df73c623b2
commit aff6b7e212
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 6 deletions

View file

@ -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<typeof userEvent.setup>) => {
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(<TeamInfoView {...props} />);
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<string, unknown>;
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<string, unknown>;
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);

View file

@ -863,12 +863,8 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
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