fix(ui): preserve configured agent labels

This commit is contained in:
joshavant
2026-08-11 15:57:21 -05:00
committed by Josh Avant
parent 3851849eea
commit 2fbf4fdbe6
5 changed files with 61 additions and 13 deletions
+2 -4
View File
@@ -135,9 +135,7 @@ function sidebarAgentMenuRows(params: {
const agentId = normalizeAgentId(entry.id);
return (
agentId.toLowerCase().includes(query) ||
(params.identities.get(agentId)?.name?.trim() || normalizeAgentLabel(entry))
.toLowerCase()
.includes(query)
normalizeAgentLabel(entry, params.identities.get(agentId)).toLowerCase().includes(query)
);
});
return { rows, showFilter: true };
@@ -164,7 +162,7 @@ function sidebarAgentMenuRows(params: {
function renderAgentRow(agent: AgentMenuAgent, params: SidebarAgentMenuParams) {
const agentId = normalizeAgentId(agent.id);
const identity = params.identities.get(agentId) ?? null;
const label = identity?.name?.trim() || normalizeAgentLabel(agent);
const label = normalizeAgentLabel(agent, identity);
const active = agentId === params.activeId;
const unread = active ? 0 : params.agentUnreadCount(agentId);
const approvals = params.agentApprovalCount(agentId);
+1 -2
View File
@@ -82,8 +82,7 @@ export function renderAppSidebarBrand(host: AppSidebarRenderHost) {
const agentId = normalizeAgentId(entry.id);
return agentId !== cardAgentId && host.agentUnreadCount(agentId) > 0;
});
const cardName =
cardIdentity?.name?.trim() || (cardAgent ? normalizeAgentLabel(cardAgent) : cardAgentId);
const cardName = cardAgent ? normalizeAgentLabel(cardAgent, cardIdentity) : cardAgentId;
const approvalCount = host.sessionData.approvalBadgeSnapshot().agentCounts.get(cardAgentId) ?? 0;
const gateway = host.sessionDataContext?.gateway;
const avatarAuthToken = gateway
+1 -1
View File
@@ -84,7 +84,7 @@ export function renderSidebarAgentMenuForController(controller: SidebarMenusCont
position,
basePath: host.basePath,
activeId,
activeName: identity?.name?.trim() || (agent ? normalizeAgentLabel(agent) : activeId),
activeName: agent ? normalizeAgentLabel(agent, identity) : activeId,
agents,
identities,
filter: controller.agentMenuFilter,
+11 -6
View File
@@ -23,6 +23,8 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "../str
type AgentRosterEntry = {
id: string;
kind?: "agent" | "system";
name?: string;
identity?: { name?: string };
};
/** Ordinary agent targets; system rows remain available to diagnostic surfaces. */
@@ -319,13 +321,16 @@ type ConfigSnapshot = {
};
};
export function normalizeAgentLabel(agent: {
id: string;
name?: string;
identity?: { name?: string };
}) {
export function normalizeAgentLabel(
agent: AgentRosterEntry,
hydratedIdentity?: { name?: string } | null,
) {
// Roster labels own operator target identity; workspace identity only fills gaps.
return (
normalizeOptionalString(agent.name) ?? normalizeOptionalString(agent.identity?.name) ?? agent.id
normalizeOptionalString(agent.name) ??
normalizeOptionalString(agent.identity?.name) ??
normalizeOptionalString(hydratedIdentity?.name) ??
agent.id
);
}
@@ -52,6 +52,52 @@ describe("AppSidebar agent chip", () => {
expect(request).toHaveBeenCalledWith("agent.identity.get", { agentId: "main" });
});
it("keeps the configured roster label when identity hydration returns a fallback", async () => {
const request = vi.fn(async (_method: string, params: { agentId: string }) =>
params.agentId === "main"
? { agentId: "main", name: "Workspace Molty", avatar: "🦞" }
: { agentId: "rust-claw", name: "Assistant", avatar: "🦀" },
);
const gatewayHarness = createGatewayHarness({ request } as unknown as GatewayBrowserClient);
const agentIdentity = createAgentIdentityCapability(gatewayHarness.gateway);
const { sidebar } = await mountSidebar(
gatewayHarness.gateway,
createSessions("main", ["agent:main:main"]),
"panel",
{
defaultId: "main",
mainKey: "main",
scope: "per-sender",
agents: [{ id: "main" }, { id: "rust-claw", name: "rust-claw" }],
},
[],
agentIdentity,
);
sidebar.connected = true;
await vi.waitFor(() => {
expect(sidebar.querySelector(".sidebar-agent-card__name")?.textContent?.trim()).toBe(
"Workspace Molty",
);
});
sidebar.querySelector<HTMLButtonElement>(".sidebar-agent-card__main")?.click();
await vi.waitFor(() => {
expect(request).toHaveBeenCalledWith("agent.identity.get", { agentId: "rust-claw" });
const labels = [
...sidebar.querySelectorAll(".sidebar-agent-menu .agent-select__option-label"),
].map((element) => element.textContent?.trim());
expect(labels).toEqual(["Workspace Molty", "rust-claw"]);
});
await vi.waitFor(() => {
const rustRow = [
...sidebar.querySelectorAll<HTMLElement>(".sidebar-agent-menu__agent-switch"),
].find((row) => row.textContent?.includes("rust-claw"));
expect(
rustRow?.querySelector(".agent-select__avatar--text")?.getAttribute("data-avatar"),
).toBe("🦀");
});
});
it("hydrates agents added while the switcher remains open", async () => {
const request = vi.fn(async (_method: string, params: { agentId: string }) => ({
agentId: params.agentId,