import type { PieceSummary } from '../api'; export interface SplitPieces { defaults: PieceSummary[]; customs: PieceSummary[]; } /** * Split a flat list of PieceSummary into Default (built-in) and Custom sections. * Pieces with source === 'builtin' go to defaults; all others go to customs. */ export function splitPieces(pieces: PieceSummary[]): SplitPieces { const defaults: PieceSummary[] = []; const customs: PieceSummary[] = []; for (const p of pieces) { if (p.source === 'builtin') { defaults.push(p); } else { customs.push(p); } } return { defaults, customs }; } /** * De-duplicate a flat list of PieceSummary by name, keeping the highest-priority * source for each name — mirroring the executor's resolution order: * user-custom > global-custom > builtin * * Use this wherever a piece is SELECTED TO RUN (task creation, scheduled tasks, * continue-with-piece dialogs). The result matches what the executor will actually * run, so the user sees exactly one "chat" entry rather than two. * * Do NOT use this in the management/PiecesPage view — that view intentionally * shows both the builtin and any same-named custom side-by-side. */ export function resolvePieceOptions(pieces: PieceSummary[]): PieceSummary[] { const PRIORITY: Record = { 'user-custom': 0, 'global-custom': 1, builtin: 2, }; const seen = new Map(); for (const p of pieces) { const existing = seen.get(p.name); const pPrio = PRIORITY[p.source ?? 'builtin'] ?? 2; if (!existing) { seen.set(p.name, p); } else { const existingPrio = PRIORITY[existing.source ?? 'builtin'] ?? 2; if (pPrio < existingPrio) { seen.set(p.name, p); } } } return Array.from(seen.values()); }