From 96c9b00d927d9be328dbf9a8730d97416029e320 Mon Sep 17 00:00:00 2001 From: iliya Date: Sun, 15 Mar 2026 11:18:49 +0200 Subject: [PATCH] feat: implement unconditionally dropping of CLI provisioner artifacts - Added a new function to drop CLI provisioner members from the member map, ensuring these internal artifacts are never displayed to users. - Updated the createCliProvisionerNameGuard function to unconditionally hide provisioner names, regardless of the presence of base members. - Modified unit tests to reflect the new behavior of dropping provisioner names even when the base member is absent. --- .../services/team/TeamMemberLogsFinder.ts | 3 +++ src/main/workers/team-fs-worker.ts | 21 +++++++++++++++++++ src/shared/utils/teamMemberName.ts | 21 +++++++------------ test/shared/utils/teamMemberName.test.ts | 4 ++-- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/main/services/team/TeamMemberLogsFinder.ts b/src/main/services/team/TeamMemberLogsFinder.ts index 28bc7200..87459242 100644 --- a/src/main/services/team/TeamMemberLogsFinder.ts +++ b/src/main/services/team/TeamMemberLogsFinder.ts @@ -370,6 +370,9 @@ export class TeamMemberLogsFinder { results.length = 0; results.push(...nonSubagent, ...subagentsByKey.values()); } + // NOTE: dedup assumes cumulative snapshots (largest file = superset of all smaller ones). + // Safety net: filterChunksByWorkIntervals on frontend still filters content by time, + // so even if the wrong file is picked, only task-relevant chunks are shown. const sorted = results.sort( (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime() diff --git a/src/main/workers/team-fs-worker.ts b/src/main/workers/team-fs-worker.ts index 61324be3..d0fa49c6 100644 --- a/src/main/workers/team-fs-worker.ts +++ b/src/main/workers/team-fs-worker.ts @@ -285,6 +285,26 @@ function dropCliAutoSuffixedMembers( } } +const PROVISIONER_SUFFIX = '-provisioner'; + +/** + * Drop CLI provisioner artifacts ("{name}-provisioner") unconditionally. + * These are temporary internal agents created during team provisioning + * and should never be shown to the user. + */ +function dropCliProvisionerMembers( + memberMap: Map +): void { + for (const [key, member] of Array.from(memberMap.entries())) { + const lower = member.name.trim().toLowerCase(); + if (!lower.endsWith(PROVISIONER_SUFFIX)) continue; + const base = lower.slice(0, -PROVISIONER_SUFFIX.length); + if (base) { + memberMap.delete(key); + } + } +} + async function listTeams( payload: ListTeamsPayload ): Promise<{ teams: unknown[]; diag: ListTeamsDiag }> { @@ -428,6 +448,7 @@ async function listTeams( } dropCliAutoSuffixedMembers(memberMap); + dropCliProvisionerMembers(memberMap); const members = Array.from(memberMap.values()); const summary = { diff --git a/src/shared/utils/teamMemberName.ts b/src/shared/utils/teamMemberName.ts index c6f21956..f4ae1785 100644 --- a/src/shared/utils/teamMemberName.ts +++ b/src/shared/utils/teamMemberName.ts @@ -43,27 +43,20 @@ const PROVISIONER_SUFFIX = '-provisioner'; /** * Claude CLI creates temporary "{name}-provisioner" agents during team provisioning - * to spawn real teammates. These are internal artifacts and should be hidden when - * the real base member (e.g. "alice") also exists. + * to spawn real teammates. These are always internal artifacts — never real teammates. * - * Only removes "alice-provisioner" if "alice" is present — if the base is missing, - * the provisioner entry is kept for visibility. + * Unlike numeric suffixes (alice-2) which can be intentional, "-provisioner" is a + * hardcoded CLI pattern that should never be exposed to the user. We unconditionally + * hide any name ending with "-provisioner" regardless of whether the base name exists. */ export function createCliProvisionerNameGuard( - allNames: Iterable + _allNames: Iterable ): (name: string) => boolean { - const allLower = new Set(); - for (const n of allNames) { - if (typeof n !== 'string') continue; - const t = n.trim().toLowerCase(); - if (t) allLower.add(t); - } - return (name: string): boolean => { const lower = name.trim().toLowerCase(); if (!lower.endsWith(PROVISIONER_SUFFIX)) return true; const base = lower.slice(0, -PROVISIONER_SUFFIX.length); - if (!base) return true; - return !allLower.has(base); + // Keep bare "-provisioner" (no base) — that's not a CLI artifact pattern + return !base; }; } diff --git a/test/shared/utils/teamMemberName.test.ts b/test/shared/utils/teamMemberName.test.ts index baf38862..bb121b20 100644 --- a/test/shared/utils/teamMemberName.test.ts +++ b/test/shared/utils/teamMemberName.test.ts @@ -56,10 +56,10 @@ describe('createCliProvisionerNameGuard', () => { expect(keep('bob-provisioner')).toBe(false); }); - it('keeps provisioner names when the base member is absent', () => { + it('drops provisioner names even when the base member is absent', () => { const keep = createCliProvisionerNameGuard(['carol-provisioner']); - expect(keep('carol-provisioner')).toBe(true); + expect(keep('carol-provisioner')).toBe(false); }); it('treats base-name collisions case-insensitively', () => {