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.
This commit is contained in:
parent
65a9928cb5
commit
96c9b00d92
4 changed files with 33 additions and 16 deletions
|
|
@ -370,6 +370,9 @@ export class TeamMemberLogsFinder {
|
||||||
results.length = 0;
|
results.length = 0;
|
||||||
results.push(...nonSubagent, ...subagentsByKey.values());
|
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(
|
const sorted = results.sort(
|
||||||
(a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
|
(a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
|
||||||
|
|
|
||||||
|
|
@ -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<string, { name: string; role?: string; color?: string }>
|
||||||
|
): 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(
|
async function listTeams(
|
||||||
payload: ListTeamsPayload
|
payload: ListTeamsPayload
|
||||||
): Promise<{ teams: unknown[]; diag: ListTeamsDiag }> {
|
): Promise<{ teams: unknown[]; diag: ListTeamsDiag }> {
|
||||||
|
|
@ -428,6 +448,7 @@ async function listTeams(
|
||||||
}
|
}
|
||||||
|
|
||||||
dropCliAutoSuffixedMembers(memberMap);
|
dropCliAutoSuffixedMembers(memberMap);
|
||||||
|
dropCliProvisionerMembers(memberMap);
|
||||||
|
|
||||||
const members = Array.from(memberMap.values());
|
const members = Array.from(memberMap.values());
|
||||||
const summary = {
|
const summary = {
|
||||||
|
|
|
||||||
|
|
@ -43,27 +43,20 @@ const PROVISIONER_SUFFIX = '-provisioner';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claude CLI creates temporary "{name}-provisioner" agents during team provisioning
|
* Claude CLI creates temporary "{name}-provisioner" agents during team provisioning
|
||||||
* to spawn real teammates. These are internal artifacts and should be hidden when
|
* to spawn real teammates. These are always internal artifacts — never real teammates.
|
||||||
* the real base member (e.g. "alice") also exists.
|
|
||||||
*
|
*
|
||||||
* Only removes "alice-provisioner" if "alice" is present — if the base is missing,
|
* Unlike numeric suffixes (alice-2) which can be intentional, "-provisioner" is a
|
||||||
* the provisioner entry is kept for visibility.
|
* 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(
|
export function createCliProvisionerNameGuard(
|
||||||
allNames: Iterable<string>
|
_allNames: Iterable<string>
|
||||||
): (name: string) => boolean {
|
): (name: string) => boolean {
|
||||||
const allLower = new Set<string>();
|
|
||||||
for (const n of allNames) {
|
|
||||||
if (typeof n !== 'string') continue;
|
|
||||||
const t = n.trim().toLowerCase();
|
|
||||||
if (t) allLower.add(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (name: string): boolean => {
|
return (name: string): boolean => {
|
||||||
const lower = name.trim().toLowerCase();
|
const lower = name.trim().toLowerCase();
|
||||||
if (!lower.endsWith(PROVISIONER_SUFFIX)) return true;
|
if (!lower.endsWith(PROVISIONER_SUFFIX)) return true;
|
||||||
const base = lower.slice(0, -PROVISIONER_SUFFIX.length);
|
const base = lower.slice(0, -PROVISIONER_SUFFIX.length);
|
||||||
if (!base) return true;
|
// Keep bare "-provisioner" (no base) — that's not a CLI artifact pattern
|
||||||
return !allLower.has(base);
|
return !base;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,10 @@ describe('createCliProvisionerNameGuard', () => {
|
||||||
expect(keep('bob-provisioner')).toBe(false);
|
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']);
|
const keep = createCliProvisionerNameGuard(['carol-provisioner']);
|
||||||
|
|
||||||
expect(keep('carol-provisioner')).toBe(true);
|
expect(keep('carol-provisioner')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('treats base-name collisions case-insensitively', () => {
|
it('treats base-name collisions case-insensitively', () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue