AI enforcement, model gating, and bonus credit packs
Share
Opie's daily request limit is now actually enforced — no more silently uncapped usage — and you can buy AI credit packs to keep going on the days you blow past the cap. We also added plan-aware model gating so cheaper plans can't accidentally trigger expensive models, and Settings now shows your real daily AI count with a Buy More button right on the AI Usage and Billing tabs.
- Daily AI request limit is enforced for real now: when you hit your plan's cap, AI calls return a clear 'limit reached' response with the exact reset time instead of silently going through.
- New AI credit packs in Stripe — Starter (500 requests, $5), Pro (2,000 requests, $15), Studio (10,000 requests, $49). Bonus credits stack on top of your daily plan limit and never expire.
- Settings → Billing and Settings → AI Usage both show your daily count today (e.g., '18 / 25 requests today') with a + N bonus credits chip and a Buy More button that opens a one-click Stripe checkout.
- Plan-aware model gating: Free and Garage plans get Gemini 2.5 Flash, Shop adds Gemini 2.5 Pro and GPT-4o Mini, Floor unlocks the full lineup. Disallowed model requests silently downgrade to your plan's best model instead of hard-blocking.
- After buying credits you'll see a 'Credits added' banner in Settings and the meter refreshes automatically as Stripe confirms the payment.
Details for builders
Replaced the stub in lib/server/billing/enforceLimits.ts with a real org_usage_daily query plus org_billing.ai_bonus_credits lookup; the gateway now throws AI_DAILY_LIMIT_EXCEEDED with current, limit, planLimit, bonusCredits, and resetAt fields, surfaced as HTTP 429 in the loop-assistant and conversations send routes. Added getAllowedModels and resolveModel helpers in lib/ai/aiRouter.ts that downgrade requested models to the plan's default and log the downgrade. Added the org_billing.ai_bonus_credits column (migration 20260504120000), three Stripe one-time prices for credit packs, the new POST /api/billing/buy-ai-credits route, a credit_pack branch in the Stripe webhook with audit-log idempotency, and bonus-credit deduction in trackOrgUsage that fires only when the daily cap is exceeded. UI: components/billing/BuyAICreditsDialog.tsx pack picker, BillingSettings now reads ai_requests_daily and ai_bonus_credits from /api/billing/usage and renders the meter + Buy More CTA, AIUsageSettings adds a top-of-page 'Your Plan's Daily AI Limit' card with the same dialog and renames the existing combined card to 'Provider Rate Limits' to disambiguate.
- Migration 20260504120000_org_billing_ai_bonus_credits.sql adds ai_bonus_credits INTEGER NOT NULL DEFAULT 0 to org_billing.
- checkAIRequestLimit returns AIRequestLimitResult with planLimit, bonusCredits, resetAt (next UTC midnight), and code AI_DAILY_LIMIT_EXCEEDED.
- aiGateway calls checkAIRequestLimit before routing for platform_paid mode and throws a typed error rather than going through enforceOrgLimits('ai_request') for the daily cap.
- loop-assistant and conversations/[id]/send routes return HTTP 429 with { code, current, limit, planLimit, bonusCredits, resetAt, upgradeUrl } on AI_DAILY_LIMIT_EXCEEDED.
- aiRouter.runModel accepts an optional planTier (defaults to 'free') and runs the requested model through resolveModel before dispatch.
- New STRIPE_PRICE_AI_CREDITS_500/2000/10000 env var contract; live-mode prices created via Stripe MCP.
- POST /api/billing/buy-ai-credits validates org membership, creates a mode='payment' Checkout Session with metadata.credit_pack='true' and returns { url }.
- Stripe webhook handleCheckoutCompleted now branches on metadata.credit_pack and increments org_billing.ai_bonus_credits via applyCreditPackToOrg, with an audit_events guard against retried webhook deliveries.
- trackOrgUsage('ai_request') now decrements ai_bonus_credits by the count of requests that overflow max_ai_requests_per_day.
- /api/billing/usage now selects and returns billing.ai_bonus_credits.
- BillingSettings replaces 'AI Requests (This Month)' with 'AI Requests Today' against the daily cap, adds the bonus-credits chip + Buy More button, mounts BuyAICreditsDialog, and shows a one-time 'Credits added' banner on ?credits=purchased.
- AIUsageSettings adds 'Your Plan's Daily AI Limit' as the first card and renames the existing combined card to 'Provider Rate Limits' for clarity.
- lib/constants/pricingPlans.ts exports AI_CREDIT_PACKS keyed by '500' | '2000' | '10000' with priceEnvVar bindings.