export class MemberValidationError extends Error { constructor( message: string, public readonly memberName?: string, public readonly issue?: string, ) { super(message) this.name = "MemberValidationError" } } function translateMemberError( input: Record, agentEligibilityRegistry: Readonly>, ): MemberValidationError { const name = typeof input.name === "string" ? input.name : "" const hasCategory = input.category != null const hasSubagentType = input.subagent_type != null const hasKind = input.kind === "category" || input.kind === "subagent_type" if (hasCategory && hasSubagentType) { return new MemberValidationError( `Member '${name}' specifies both 'category' and 'subagent_type'. Must specify exactly one via 'kind' discriminator.`, name, "both-kinds", ) } if (!hasKind && !hasCategory && !hasSubagentType) { return new MemberValidationError( `Member '${name}' missing 'kind' discriminator. Specify either {kind:'category', category, prompt} or {kind:'subagent_type', subagent_type}.`, name, "missing-kind", ) } if (input.kind === "category" || (!hasKind && hasCategory)) { const category = typeof input.category === "string" ? input.category : "" return new MemberValidationError( `Member '${name}' uses category '${category}' but is missing required 'prompt' field. Category members must supply a task prompt.`, name, "category-missing-prompt", ) } if (input.kind === "subagent_type" || (!hasKind && hasSubagentType)) { const subagentType = typeof input.subagent_type === "string" ? input.subagent_type : String(input.subagent_type) if (typeof input.subagent_type !== "string" || !agentEligibilityRegistry[input.subagent_type]) { return new MemberValidationError( `Unknown subagent_type '${subagentType}'. Available ELIGIBLE agents: sisyphus, atlas, sisyphus-junior, hephaestus (if D-36 applied). Use delegate-task for read-only agents like oracle, librarian, explore, metis, momus, multimodal-looker.`, name, "unknown-subagent", ) } } return new MemberValidationError(`Member '${name}' validation failed.`, name, "zod-residual") } export function createParseMember( memberSchema: { safeParse(input: unknown): { success: true; data: TMember } | { success: false } }, agentEligibilityRegistry: Readonly>, ): (input: unknown) => TMember { return function parseMember(input: unknown) { if (input == null || typeof input !== "object") { throw new MemberValidationError("Member must be an object") } const raw = input as Record const result = memberSchema.safeParse( raw.kind === undefined && (raw.category !== undefined || raw.subagent_type !== undefined) ? { ...raw, kind: raw.category !== undefined ? "category" : "subagent_type" } : raw, ) if (!result.success) { throw translateMemberError(raw, agentEligibilityRegistry) } return result.data } }