fix(tui): treat Name-N HUD labels as echoed spawn ids
Collision suffixes such as HindsightMcpFunnel-3 were still shown as Name-3: Name because the first pass only compared the raw id.
This commit is contained in:
@@ -450,6 +450,13 @@ const MODEL_CYCLE_TRACK_CLEAR_MS = 4000;
|
||||
const SUBAGENT_HUD_VISIBLE_LIMIT = 8;
|
||||
const SUBAGENT_OBSERVER_UI_COALESCE_MS = 100;
|
||||
|
||||
/** True when `label` is the spawn handle, including collision suffixes (`Name-2`). */
|
||||
function hudLabelEchoesId(id: string, label: string): boolean {
|
||||
if (label.localeCompare(id, undefined, { sensitivity: "accent" }) === 0) return true;
|
||||
const suffix = id.startsWith(`${label}-`) ? id.slice(label.length + 1) : "";
|
||||
return /^\d+$/.test(suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the anchored subagent HUD block: a bold accent "Subagents" header plus
|
||||
* a bounded set of running-agent rows in the same `Id ⟨role⟩: description` shape
|
||||
@@ -480,10 +487,7 @@ export function renderSubagentHudLines(sessions: ObservableSession[], columns: n
|
||||
const badge = agentTypeBadge(role, theme);
|
||||
let line = `${dot} ${theme.fg("accent", theme.bold(displayId))}${badge}`;
|
||||
const description = session.description?.trim() || session.progress?.description?.trim();
|
||||
const distinctDescription =
|
||||
description && description.localeCompare(session.id, undefined, { sensitivity: "accent" }) !== 0
|
||||
? description
|
||||
: undefined;
|
||||
const distinctDescription = description && !hudLabelEchoesId(session.id, description) ? description : undefined;
|
||||
if (distinctDescription) {
|
||||
const budget = Math.max(
|
||||
TRUNCATE_LENGTHS.SHORT,
|
||||
@@ -494,7 +498,7 @@ export function renderSubagentHudLines(sessions: ObservableSession[], columns: n
|
||||
// No spawn description: fall back to a muted task preview, same as
|
||||
// the inline task rows when a row has no label.
|
||||
const taskPreview = session.progress?.task?.trim();
|
||||
if (taskPreview && taskPreview.localeCompare(session.id, undefined, { sensitivity: "accent" }) !== 0) {
|
||||
if (taskPreview && !hudLabelEchoesId(session.id, taskPreview)) {
|
||||
line += ` ${theme.fg("muted", truncateToWidth(replaceTabs(taskPreview), TRUNCATE_LENGTHS.SHORT))}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,17 @@ describe("subagent HUD lines", () => {
|
||||
expect(echoed).toMatch(/HindsightMcpFunnel.*scout/);
|
||||
expect(echoed).not.toContain("HindsightMcpFunnel: HindsightMcpFunnel");
|
||||
|
||||
const collision = render([
|
||||
makeSession({
|
||||
id: "HindsightMcpFunnel-3",
|
||||
agent: "scout",
|
||||
description: "HindsightMcpFunnel",
|
||||
}),
|
||||
]);
|
||||
expect(collision).toContain("HindsightMcpFunnel-3");
|
||||
expect(collision).toMatch(/HindsightMcpFunnel-3.*scout/);
|
||||
expect(collision).not.toContain("HindsightMcpFunnel-3: HindsightMcpFunnel");
|
||||
|
||||
const defaultWorker = render([makeSession({ id: "AuthLoader", agent: "task", description: "Refactor auth" })]);
|
||||
expect(defaultWorker).toContain("AuthLoader: Refactor auth");
|
||||
expect(defaultWorker).not.toMatch(/AuthLoader.*task/);
|
||||
|
||||
Reference in New Issue
Block a user