import { env } from "cloudflare:workers";
import { stripeEnabled } from "../billing/config";
import { MARKUP, METER_PRICES } from "~/lib/pricing";
import {
commitUsage,
PlatformLimitError,
QuotaExceededError,
releaseUsage,
remainingQuota,
reserveUsage,
} from "./quota";
import type { Reservation } from "./quota";
import { isRecorded, recordUsage } from "./record";
import { captureServerEvent } from "../analytics";
import { addCounter, counterTotals } from "../counters";
export type AiMeterContext = {
accountId: string;
repoId?: string | null;
/** The feature making the calls, e.g. "mergefix" or "clef". */
feature: string;
/** A durable id for the unit of work (run id); call keys are `ai:<scope>:<index>`. */
scope: string;
};
export type AiTokens = { inputTokens: number; outputTokens: number };
/** Tokens held for a call before it starts (or what is left, if less); afterCall settles it at the real count. */
export const AI_TOKEN_HOLD = 8_000;
const AI_TOKEN_HOLD_TTL_MS = 15 * 60 * 1000;
const DEFAULT_DAILY_CEILING_CENTS = 10_000;
/** The ceiling in cents, null when off, or NaN when misconfigured (which refuses every call). */
export function dailyAiCeilingCents(raw = env.AI_DAILY_SPEND_CEILING_CENTS): number | null {
// Unset: on for Stripe-billed instances, off for self-hosted ones that never opted in.
if (raw === undefined || raw.trim() === "") return stripeEnabled() ? DEFAULT_DAILY_CEILING_CENTS : null;
if (raw.trim().toLowerCase() === "off") return null;
const cents = Number(raw);
return Number.isFinite(cents) && cents >= 0 ? cents : Number.NaN;
}