export type AiQuotaPaywallReason = | 'credit_limit_exceeded' | 'deploy_quota_exceeded' | 'dollar_commit_exhausted' | 'no_seat_assigned' | 'payment_past_due' | 'provisioning_error' | 'token_limit_exceeded' | 'trial_expired' | 'user_credit_limit_exceeded'; /** * The complete set of paywall reasons, kept in lockstep with the * `AiQuotaPaywallReason` union above. Shared so that callers parsing * untrusted error payloads (`packages/vite-plugin-file-sync`) can validate * string values against the same source of truth rather than duplicating it. */ export const AI_QUOTA_PAYWALL_REASONS: ReadonlySet = new Set([ 'credit_limit_exceeded', 'deploy_quota_exceeded', 'dollar_commit_exhausted', 'no_seat_assigned', 'payment_past_due', 'provisioning_error', 'token_limit_exceeded', 'trial_expired', 'user_credit_limit_exceeded' ]); export const isAiQuotaPaywallReason = (value: unknown): value is AiQuotaPaywallReason => typeof value === 'string' && AI_QUOTA_PAYWALL_REASONS.has(value as AiQuotaPaywallReason); export const getAiQuotaPaywallReasonFromMessage = (message: string): AiQuotaPaywallReason | undefined => { const normalizedMessage = message.toLowerCase(); if ( normalizedMessage.includes('no_seat_assigned') || normalizedMessage.includes("don't have an ai builder license assigned") || normalizedMessage.includes("doesn't have an ai builder license assigned") || normalizedMessage.includes("doesn't have an ai builder seat assigned") || normalizedMessage.includes('does not have an ai builder license assigned') || normalizedMessage.includes('does not have an ai builder seat assigned') || normalizedMessage.includes("don't have an ai builder seat assigned") || normalizedMessage.includes('no builder seat assigned') ) { return 'no_seat_assigned'; } if ( normalizedMessage.includes('payment_past_due') || normalizedMessage.includes('payment is past due') || normalizedMessage.includes('subscription payment failed') ) { return 'payment_past_due'; } if ( normalizedMessage.includes('trial_expired') || normalizedMessage.includes('ai trial period has ended') || normalizedMessage.includes('free trial has ended') ) { return 'trial_expired'; } // A paid org whose entitlement row hasn't been written yet (webhook delayed or // failed). Must be matched before the generic 'ai usage limit' fallback below — // this state is a provisioning problem, not exhaustion, and its paywall must not // suggest buying more credits. if ( normalizedMessage.includes('provisioning_error') || normalizedMessage.includes("hasn't finished provisioning") || normalizedMessage.includes('has not finished provisioning') ) { return 'provisioning_error'; } if (normalizedMessage.includes('token_limit_exceeded')) { return 'token_limit_exceeded'; } if (normalizedMessage.includes('deploy_quota_exceeded')) { return 'deploy_quota_exceeded'; } if (normalizedMessage.includes('dollar_commit_exhausted')) { return 'dollar_commit_exhausted'; } if (normalizedMessage.includes('user_credit_limit_exceeded') || normalizedMessage.includes("you've reached your assigned usage limit")) { return 'user_credit_limit_exceeded'; } if (normalizedMessage.includes('credit_limit_exceeded') || normalizedMessage.includes('organization has reached its ai usage limit')) { return 'credit_limit_exceeded'; } if (normalizedMessage.includes('ai usage limit')) { return 'token_limit_exceeded'; } return undefined; }; export const AI_QUOTA_PAYWALL_STRIPE_CHECKOUT_URL = 'https://buy.stripe.com/7sYeVc8XNfl3fZ40FT8EM02'; export const AI_QUOTA_PAYWALL_CONTACT_SALES_EMAIL = 'sales@superblockshq.com'; export const AI_QUOTA_PAYWALL_CONTACT_SALES_URL = 'https://www.superblocks.com/book-a-demo'; export const AI_QUOTA_PAYWALL_REASON_CONFIG: Record = { credit_limit_exceeded: { title: 'Your organization has reached its AI usage limit', subtitle: 'Ask your admin to purchase additional GAUs to continue using Clark AI.' }, no_seat_assigned: { title: "You don't have an AI Builder license", subtitle: 'Ask your admin to assign a license so you can continue building with Clark AI.' }, token_limit_exceeded: { title: 'Your organization has reached its AI usage limit', subtitle: 'Upgrade your plan to continue using Clark AI and other AI-powered features.' }, trial_expired: { title: 'Your free trial has ended', subtitle: 'Upgrade to keep building with AI.' }, deploy_quota_exceeded: { title: 'Deploy limit reached', subtitle: 'Your organization has reached its deployed app limit. Add more deployed apps to continue.' }, dollar_commit_exhausted: { title: 'Dollar commit pool exhausted', subtitle: 'Your organization has used its entire dollar commit pool. Contact your account team to adjust your contract.' }, payment_past_due: { title: "Your last payment didn't go through", subtitle: 'Update your payment method to restore access to Clark AI and other paid features. Your subscription is paused until the outstanding invoice is paid.' }, user_credit_limit_exceeded: { title: "You've reached your assigned usage limit", subtitle: 'Contact an admin to request an increase.' }, provisioning_error: { title: "Your organization's plan isn't fully set up yet", subtitle: "Your subscription is active, but its usage allowance hasn't finished provisioning. Try again in a few minutes — if this persists, contact support@superblocks.com." } }; export const AI_QUOTA_PAYWALL_PRICE_PER_SEAT_ANNUAL = 100; export const AI_QUOTA_PAYWALL_PRICE_PER_SEAT_MONTHLY = 125; export const AI_QUOTA_PAYWALL_CREDITS_PER_SEAT = 100; export const AI_QUOTA_PAYWALL_APPS_PER_SEAT = 1; export const AI_QUOTA_PAYWALL_MIN_SEATS = 1; export const AI_QUOTA_PAYWALL_MAX_SEATS = 100; export const AI_QUOTA_PAYWALL_TEAMS_FEATURES = [ '100 GAUs per AI Builder / month', 'Org-level GAU top-ups, shared across team', 'Clark AI agent for app building', 'Clark User Memory', 'GAU Packs add-on available' ]; export const AI_QUOTA_PAYWALL_ENTERPRISE_FEATURES = [ 'Clark Integration and Organization memory', 'Source Control via GitHub, GitLab, BitBucket, Azure DevOps', 'Secrets Management', 'Embedded Applications', 'Custom app hosting' ]; export const AI_QUOTA_PAYWALL_TEAMS_PLATFORM_FEATURES = ['Staging and production environments', '1 deployed app per team']; export const AI_QUOTA_PAYWALL_ENTERPRISE_PLATFORM_FEATURES = [ 'Deploy in your VPC (Hybrid or Cloud Prem)', 'SSO / SAML / OIDC', 'Role-based access control (RBAC)', 'Audit Logs & Observability' ]; export const AI_QUOTA_PAYWALL_ENTERPRISE_SUPPORT_FEATURES = ['Dedicated account team', 'Enterprise Uptime and SLAs'];