From 678d29b57421cb47f0318ad98cf646206158f453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 7 Aug 2026 08:31:03 +0000 Subject: [PATCH] refuse a duplicate agent name in the panel, not in a 500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server's uniqueness check is the one that matters, but its 409 currently never fires (the constraint name is on err.cause, not in the DrizzleQueryError message), so a collision came back as "Internal Server Error". The address book is already in hand here — checking it first turns the common case into a sentence the human can act on. Diagnosis of the server-side bug, with the patch, is in COMMS/agent-panels-split-2026-08-07.md; that file belongs to another agent and is still uncommitted, so it is theirs to apply. --- .../officerdev/src/apps/Chat/useAgentPanel.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/workspaces/officerdev/src/apps/Chat/useAgentPanel.ts b/src/workspaces/officerdev/src/apps/Chat/useAgentPanel.ts index d806743b..383b02f8 100644 --- a/src/workspaces/officerdev/src/apps/Chat/useAgentPanel.ts +++ b/src/workspaces/officerdev/src/apps/Chat/useAgentPanel.ts @@ -84,8 +84,14 @@ export function useAgentPanel(panelId: string) { }, [agent?.id, agent?.panelId, panelId, dashboardId, queryClient]); const claim = useMutation({ - mutationFn: (input: { name: string; cwd?: string; rolePrompt?: string }) => - clientRef.current.post<{ agent: AgentPanelView; created: boolean }>('/chat/agent-panels', { + mutationFn: (input: { name: string; cwd?: string; rolePrompt?: string }) => { + // Reject a name already on this dashboard before asking the server. The uniqueness that matters is + // the server's — an address that resolved to two agents would make a handoff ambiguous — but the + // list is already in hand, and the answer here is a sentence the human can act on. + if (agents.some((a) => a.name === input.name)) { + return Promise.reject(new Error(`There is already an agent named "${input.name}" on this dashboard`)); + } + return clientRef.current.post<{ agent: AgentPanelView; created: boolean }>('/chat/agent-panels', { dashboardId, panelId, // Record the directory the panel is already scoped to. A row with no cwd runs incoming handoffs @@ -93,7 +99,8 @@ export function useAgentPanel(panelId: string) { // agent working in two places depending on who spoke to it. ...(cwd && cwd !== '~' ? { cwd } : {}), ...input, - }), + }); + }, onSuccess: ({ agent: created }) => { // Write the name into the panel before invalidating: the config is what survives a drag, and a // refetch that landed first would show an agent this panel does not yet claim.