export const SUBAGENT_MODES = ["single", "parallel", "chain"] as const; export type SubagentMode = (typeof SUBAGENT_MODES)[number]; export interface SubagentRunResult { task: string; exitCode: number; agent: string; output: string; error?: string; elapsedMs: number; model?: string; tools?: string; } export interface IndexedSubagentRunResult extends SubagentRunResult { taskIndex: number; } export interface SubagentResultDetails { mode: SubagentMode; tasks: IndexedSubagentRunResult[]; } export function normalizeSubagentRunResult( value: Partial & Pick, ): SubagentRunResult { return { task: String(value.task), exitCode: Number.isFinite(value.exitCode) ? Number(value.exitCode) : 1, agent: typeof value.agent === "string" && value.agent.trim() ? value.agent : "subagent", output: String(value.output || ""), error: typeof value.error === "string" && value.error ? value.error : undefined, elapsedMs: Number.isFinite(value.elapsedMs) ? Math.max(0, Number(value.elapsedMs)) : 0, model: typeof value.model === "string" && value.model ? value.model : undefined, tools: typeof value.tools === "string" && value.tools ? value.tools : undefined, }; } export function makeSubagentResultDetails(mode: SubagentMode, tasks: SubagentRunResult[]): SubagentResultDetails { return { mode, tasks: tasks.map((task, index) => ({ ...task, taskIndex: index + 1 })), }; } export function isSubagentRunResult(value: unknown): value is SubagentRunResult { if (!value || typeof value !== "object") return false; const candidate = value as Partial; if (typeof candidate.task !== "string") return false; if (typeof candidate.agent !== "string") return false; if (typeof candidate.output !== "string") return false; if (typeof candidate.exitCode !== "number" || !Number.isFinite(candidate.exitCode)) return false; if (typeof candidate.elapsedMs !== "number" || !Number.isFinite(candidate.elapsedMs)) return false; if (candidate.error !== undefined && typeof candidate.error !== "string") return false; if (candidate.model !== undefined && typeof candidate.model !== "string") return false; if (candidate.tools !== undefined && typeof candidate.tools !== "string") return false; return true; } export function isSubagentResultDetails(value: unknown): value is SubagentResultDetails { if (!value || typeof value !== "object") return false; const candidate = value as Partial; if (typeof candidate.mode !== "string" || !(SUBAGENT_MODES as readonly string[]).includes(candidate.mode)) { return false; } if (!Array.isArray(candidate.tasks)) return false; return candidate.tasks.every((task) => { if (!task || typeof task !== "object") return false; const item = task as Partial; return isSubagentRunResult(item) && typeof item.taskIndex === "number" && Number.isFinite(item.taskIndex); }); }