/** * Pure cost aggregation over observer episodes. * * Two layers: * - episodeUsd(episode, pricing) — compute 5-component USD breakdown per episode. * - aggregateDay(episodes, dateISO, pricing) — sum across episodes whose * started_at begins with dateISO ("YYYY-MM-DD"). * * Spec: docs/superpowers/specs/2026-05-24-llm-first-router-overhaul-design.md §12. * Consumed by `cost-stop-hook.mjs` and `/brain-retro` aggregations. */ export function episodeUsd(episode, pricing) { const tc = (episode && episode.task_cost) || {}; const classifier_usd = (tc.classifier_input_tokens || 0) * pricing.sonnet.input + (tc.classifier_output_tokens || 0) * pricing.sonnet.output; const self_assessment_usd = (tc.self_assessment_input_tokens || 0) * pricing.opus.input + (tc.self_assessment_output_tokens || 0) * pricing.opus.output; const reviewer_subagent_usd = tc.reviewer_subagent_usd || 0; const reviewer_direct_fallback_usd = tc.reviewer_direct_fallback_usd || 0; const self_retrospect_usd = tc.self_retrospect_usd || 0; const judge_spend_usd = tc.judge_spend_usd || 0; const total_usd = classifier_usd + self_assessment_usd + reviewer_subagent_usd + reviewer_direct_fallback_usd + self_retrospect_usd + judge_spend_usd; return { classifier_usd, self_assessment_usd, reviewer_subagent_usd, reviewer_direct_fallback_usd, self_retrospect_usd, judge_spend_usd, total_usd, }; } export function aggregateDay(episodes, dateISO, pricing) { const totals = { classifier_usd: 0, self_assessment_usd: 0, reviewer_subagent_usd: 0, reviewer_direct_fallback_usd: 0, self_retrospect_usd: 0, judge_spend_usd: 0, }; let episode_count = 0; for (const ep of Array.isArray(episodes) ? episodes : []) { if (!ep || !ep.timestamps || typeof ep.timestamps.started_at !== 'string') continue; if (!ep.timestamps.started_at.startsWith(dateISO)) continue; const u = episodeUsd(ep, pricing); totals.classifier_usd += u.classifier_usd; totals.self_assessment_usd += u.self_assessment_usd; totals.reviewer_subagent_usd += u.reviewer_subagent_usd; totals.reviewer_direct_fallback_usd += u.reviewer_direct_fallback_usd; totals.self_retrospect_usd += u.self_retrospect_usd; totals.judge_spend_usd += u.judge_spend_usd; episode_count += 1; } const total_usd = totals.classifier_usd + totals.self_assessment_usd + totals.reviewer_subagent_usd + totals.reviewer_direct_fallback_usd + totals.self_retrospect_usd + totals.judge_spend_usd; return { ...totals, total_usd, episode_count }; }