import type { SuperagentCurrentUsage } from '../../types'; /** * Whether the workspace has run out of builder/chat credits, mirroring the web * builder's derivation in `AppContext` (`isOutOfCredits` useMemo). Kept as a pure * function so it can be unit-tested and reused by the runtime hook. * * Out of credits when any of: * - the backend already flagged `is_over_limit`, or * - daily usage hit the daily limit and there's no gift-card balance to fall back on, or * - monthly usage hit the effective monthly limit (base monthly limit + gift-card * remaining, when a monthly limit applies). */ export function computeIsOutOfCredits(usage: SuperagentCurrentUsage | null | undefined): boolean { if (!usage) return false; const giftCardRemaining = usage.monthly_limit != null ? usage.gift_card_credit_details?.remaining ?? 0 : 0; const effectiveMonthlyLimit = (usage.monthly_limit ?? 0) + giftCardRemaining; return Boolean( usage.is_over_limit || (usage.daily_usage >= (usage.daily_limit || 1_000_000) && !giftCardRemaining) || (usage.monthly_limit != null && usage.monthly_usage >= effectiveMonthlyLimit), ); } export function isCreditSendAllowed({ isOutOfCredits, usageReady, }: { isOutOfCredits: boolean; usageReady: boolean; }): boolean { return usageReady && !isOutOfCredits; } function isDailyLimitHit(usage: SuperagentCurrentUsage): boolean { const giftCardRemaining = usage.monthly_limit != null ? usage.gift_card_credit_details?.remaining ?? 0 : 0; return usage.daily_usage >= (usage.daily_limit || 1_000_000) && !giftCardRemaining; } const usageProperties = (usage: SuperagentCurrentUsage) => ({ daily_usage: usage.daily_usage, daily_limit: usage.daily_limit, monthly_usage: usage.monthly_usage, monthly_limit: usage.monthly_limit, is_over_limit: usage.is_over_limit, }); /** Mixpanel payload matching the web OutOfCreditsMessage impression event. */ export function buildOutOfCreditsShownProperties(usage: SuperagentCurrentUsage) { const hitDailyLimit = isDailyLimitHit(usage); return { ...usageProperties(usage), hit_daily_limit: hitDailyLimit, hit_monthly_limit: !hitDailyLimit && usage.is_over_limit, }; } /** Mixpanel payload matching the web OutOfCreditsMessage upgrade CTA event. */ export function buildOutOfCreditsUpgradeProperties(usage: SuperagentCurrentUsage) { return { cta: 'View all plans', origin: 'chat out of credits message', target_route: '/billing', upgrade_type: 'billing_dialog', ...usageProperties(usage), is_daily_limit_hit: isDailyLimitHit(usage), }; }