feat(usage): report usage command quotas as percentages + honor observed provider quota resets (#5874)

* feat: report usage command quotas as percentages

Convert @@om-usage and the HTTP usage endpoint to report personal API key quotas as remaining percentages while keeping USD amounts out of the command output. Scale provider quota remaining percentages by the configured quota cutoff so the protected reserve reads as 0% left. Restore provider USD cost drilldown in the quota dashboard.\n\nAlso sync the 3.8.43 i18n changelog mirrors so the docs-sync pre-commit gate remains green.\n\nTests: DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/internal-usage-command.test.ts; DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/api-key-usage-limits.test.ts; DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/provider-window-costs.test.ts; DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/api-manager-usage-command.test.ts tests/unit/apikeys-usage-command.test.ts; npx eslint <changed files>; npm run typecheck:core; npm run build; npm run check:migration-numbering; npm run check:docs-sync; docker build --target runner-base

(cherry picked from commit f66abd2028)

* fix: honor observed provider quota resets

Detect same-resetAt quota resets when provider usage drops back to the reset floor, and prefer that observed snapshot over stale recorded weekly events for provider USD windows and API-key USD quotas.\n\nTests: npx eslint changed files\nTests: npm run typecheck:core\nTests: DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test tests/unit/lib/quota-reset-events.test.ts tests/unit/provider-window-costs.test.ts tests/unit/api-key-usage-limits.test.ts\nTests: npm run build\nTests: docker build --target runner-base --build-arg OMNIROUTE_BUILD_MEMORY_MB=4096 -t omniroute:quota-reset-window-20260702002300 .

(cherry picked from commit 39c12a6f17)

* docs(changelog): credit usage quota percentages extraction from #5863

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

---------

Co-authored-by: Wital <wital@example.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-02 00:36:57 -03:00
committed by GitHub
parent 595349a59a
commit a205cb7f5e
13 changed files with 2082 additions and 133 deletions

View File

@@ -2,6 +2,10 @@
## [Unreleased]
### ✨ New Features
- **usage (quota percentages + provider USD drilldown):** `@@om-usage` and the HTTP usage endpoint now report personal API-key quotas as **remaining percentages** (USD amounts stay out of the command output), provider quota remaining is scaled by the configured quota cutoff so the protected reserve reads as 0% left, and the quota dashboard regains a **provider USD cost drilldown** (`/api/usage/provider-window-costs` + `ProviderUsdCostModal`, management-auth gated). Also honors **observed provider quota resets**: a same-`resetAt` reset (usage dropping back to the reset floor) is detected and preferred over stale recorded weekly events for provider USD windows and API-key USD quotas. New `src/lib/usage/providerWindowCosts.ts`. Regression guards: `tests/unit/provider-window-costs.test.ts`, `tests/unit/internal-usage-command.test.ts`, `tests/unit/api-key-usage-limits.test.ts`, `tests/unit/lib/quota-reset-events.test.ts`. Extracted from [#5863](https://github.com/diegosouzapw/OmniRoute/pull/5863) by [@Witroch4](https://github.com/Witroch4).
### 🔧 Bug Fixes
- **fix(kiro):** bound the Claude model-id dash→dot normalization to a 12 digit minor so date-suffixed ids (e.g. claude-opus-4-20250514) are no longer corrupted. (thanks @voravitl)

View File

@@ -0,0 +1,296 @@
"use client";
import { useEffect, useMemo, useState } from "react";
interface ProviderWindowCostRow {
apiKeyKey: string;
apiKeyId: string | null;
apiKeyName: string;
requests: number;
totalTokens: number;
costUsd: number;
limitUsd: number | null;
limitPeriod: string | null;
limitUsedPercent: number | null;
budgetResetAt: string | null;
lastUsed: string | null;
}
interface ProviderWindowCostPayload {
provider: string;
connectionId: string | null;
windowStartAt: string;
windowResetAt: string | null;
windowSource: "provider_weekly_reset" | "fallback_rolling_7d";
windowStartSource:
| "recorded_reset_event"
| "observed_snapshot_reset"
| "inferred_from_reset_at"
| "fallback_rolling_7d";
quotaName: string | null;
quotaUsedPercent: number | null;
quotaRemainingPercent: number | null;
totalCostUsd: number;
estimatedFullQuotaUsd: number | null;
rows: ProviderWindowCostRow[];
}
interface Props {
isOpen: boolean;
onClose: () => void;
connection: any;
providerLabel: string;
accountLabel: string;
}
function formatUsd(value: number | null | undefined): string {
const numeric = Number(value || 0);
const abs = Math.abs(numeric);
const digits = abs > 0 && abs < 0.01 ? 6 : abs < 1 ? 4 : 2;
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: digits,
maximumFractionDigits: digits,
}).format(numeric);
}
function formatDateTime(value: string | null | undefined): string {
if (!value) return "unknown";
const date = new Date(value);
if (!Number.isFinite(date.getTime())) return "unknown";
return date.toLocaleString([], {
month: "short",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}
function formatPercent(value: number | null | undefined): string {
if (value === null || value === undefined || !Number.isFinite(value)) return "n/a";
return `${value.toFixed(value % 1 === 0 ? 0 : 1)}%`;
}
export default function ProviderUsdCostModal({
isOpen,
onClose,
connection,
providerLabel,
accountLabel,
}: Props) {
const [payload, setPayload] = useState<ProviderWindowCostPayload | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [simulatedPercent, setSimulatedPercent] = useState(25);
useEffect(() => {
if (!isOpen || !connection?.provider) return;
let alive = true;
async function load() {
setLoading(true);
setError(null);
try {
const params = new URLSearchParams({ provider: String(connection.provider) });
if (connection.id) params.set("connectionId", String(connection.id));
const response = await fetch(`/api/usage/provider-window-costs?${params.toString()}`);
if (!response.ok) {
const body = await response.json().catch(() => ({}));
throw new Error(body.error || `HTTP ${response.status}`);
}
const data = (await response.json()) as ProviderWindowCostPayload;
if (alive) setPayload(data);
} catch (loadError) {
if (alive) {
setError(loadError instanceof Error ? loadError.message : "Failed to load USD costs");
setPayload(null);
}
} finally {
if (alive) setLoading(false);
}
}
void load();
return () => {
alive = false;
};
}, [isOpen, connection?.id, connection?.provider]);
const maxCost = useMemo(
() => Math.max(...(payload?.rows || []).map((row) => row.costUsd), 0),
[payload]
);
const simulatedUsd =
payload?.estimatedFullQuotaUsd !== null && payload?.estimatedFullQuotaUsd !== undefined
? (payload.estimatedFullQuotaUsd * simulatedPercent) / 100
: null;
if (!isOpen) return null;
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/45 px-4 py-6"
onClick={onClose}
>
<div
className="w-full max-w-3xl max-h-[88vh] overflow-hidden rounded-lg border border-border bg-surface shadow-xl"
onClick={(event) => event.stopPropagation()}
>
<div className="flex items-start justify-between gap-4 border-b border-border px-5 py-4">
<div>
<h2 className="m-0 text-lg font-semibold text-text-main">USD Cost</h2>
<p className="mt-1 text-xs text-text-muted">
{providerLabel} · {accountLabel || connection?.id}
</p>
</div>
<button
type="button"
onClick={onClose}
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-border bg-bg-subtle text-text-main hover:bg-black/[0.04] dark:hover:bg-white/[0.04]"
aria-label="Close"
>
<span className="material-symbols-outlined text-[18px]">close</span>
</button>
</div>
<div className="overflow-y-auto px-5 py-4">
{loading ? (
<div className="flex items-center gap-2 text-sm text-text-muted">
<span className="material-symbols-outlined animate-spin text-[16px]">
progress_activity
</span>
Loading USD costs
</div>
) : error ? (
<div className="flex items-start gap-2 rounded-md border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-500">
<span className="material-symbols-outlined text-[16px]">error</span>
{error}
</div>
) : payload ? (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-2 gap-2 md:grid-cols-4">
<div className="rounded-md border border-border bg-bg-subtle px-3 py-2">
<div className="text-[10px] uppercase tracking-wide text-text-muted">Used</div>
<div className="mt-1 text-lg font-semibold tabular-nums text-text-main">
{formatUsd(payload.totalCostUsd)}
</div>
</div>
<div className="rounded-md border border-border bg-bg-subtle px-3 py-2">
<div className="text-[10px] uppercase tracking-wide text-text-muted">
Quota used
</div>
<div className="mt-1 text-lg font-semibold tabular-nums text-text-main">
{formatPercent(payload.quotaUsedPercent)}
</div>
</div>
<div className="rounded-md border border-border bg-bg-subtle px-3 py-2">
<div className="text-[10px] uppercase tracking-wide text-text-muted">
Est. 100%
</div>
<div className="mt-1 text-lg font-semibold tabular-nums text-text-main">
{payload.estimatedFullQuotaUsd === null
? "n/a"
: formatUsd(payload.estimatedFullQuotaUsd)}
</div>
</div>
<div className="rounded-md border border-border bg-bg-subtle px-3 py-2">
<div className="text-[10px] uppercase tracking-wide text-text-muted">Rows</div>
<div className="mt-1 text-lg font-semibold tabular-nums text-text-main">
{payload.rows.length}
</div>
</div>
</div>
<div className="rounded-md border border-border bg-surface px-3 py-3">
<div className="flex flex-wrap items-center justify-between gap-2 text-xs text-text-muted">
<span>
Window: {formatDateTime(payload.windowStartAt)} {" "}
{formatDateTime(payload.windowResetAt)}
</span>
<span>
{payload.windowStartSource === "recorded_reset_event"
? `From recorded ${payload.quotaName || "weekly quota"} reset`
: payload.windowStartSource === "observed_snapshot_reset"
? `From observed ${payload.quotaName || "weekly quota"} reset`
: payload.windowSource === "provider_weekly_reset"
? `From ${payload.quotaName || "weekly quota"} reset`
: "Fallback rolling 7d"}
</span>
</div>
<div className="mt-3 flex flex-col gap-2">
<div className="flex items-center justify-between gap-3 text-xs">
<span className="font-medium text-text-main">Quota estimator</span>
<span className="tabular-nums text-text-main">
{simulatedPercent}% ={" "}
{simulatedUsd === null ? "n/a" : formatUsd(simulatedUsd)}
</span>
</div>
<input
type="range"
min="1"
max="100"
step="1"
value={simulatedPercent}
onChange={(event) => setSimulatedPercent(Number(event.target.value))}
className="w-full accent-[var(--color-primary,#E54D5E)]"
disabled={payload.estimatedFullQuotaUsd === null}
/>
</div>
</div>
{payload.rows.length === 0 ? (
<div className="rounded-md border border-border px-3 py-8 text-center text-sm text-text-muted">
No API key usage in this provider window.
</div>
) : (
<div className="flex flex-col gap-2">
{payload.rows.map((row) => {
const barPercent = maxCost > 0 ? Math.max(4, (row.costUsd / maxCost) * 100) : 0;
return (
<div
key={row.apiKeyKey}
className="rounded-md border border-border bg-surface px-3 py-2"
>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="truncate text-sm font-semibold text-text-main">
{row.apiKeyName}
</div>
<div className="mt-0.5 text-[11px] text-text-muted">
{row.requests.toLocaleString()} requests ·{" "}
{row.totalTokens.toLocaleString()} tokens
</div>
</div>
<div className="text-right">
<div className="text-sm font-semibold tabular-nums text-text-main">
{formatUsd(row.costUsd)}
</div>
{row.limitUsd ? (
<div className="mt-0.5 text-[11px] tabular-nums text-text-muted">
{formatPercent(row.limitUsedPercent)} of {formatUsd(row.limitUsd)}
{row.limitPeriod ? ` ${row.limitPeriod}` : ""}
</div>
) : (
<div className="mt-0.5 text-[11px] text-text-muted">No USD limit</div>
)}
</div>
</div>
<div className="mt-2 h-2 overflow-hidden rounded-sm bg-border/60">
<div
className="h-full rounded-sm bg-[var(--color-primary,#E54D5E)]"
style={{ width: `${barPercent}%` }}
/>
</div>
</div>
);
})}
</div>
)}
</div>
) : null}
</div>
</div>
</div>
);
}

View File

@@ -1,10 +1,12 @@
"use client";
import { useMemo } from "react";
import { useMemo, useState } from "react";
import Card from "@/shared/components/Card";
import { pickDisplayValue } from "@/shared/utils/maskEmail";
import { normalizePlanTier, resolvePlanValue, worstStatus, type CardStatus } from "./utils";
import QuotaCardHeader from "./parts/QuotaCardHeader";
import QuotaCardExpanded from "./parts/QuotaCardExpanded";
import ProviderUsdCostModal from "./ProviderUsdCostModal";
const STATUS_BORDER: Record<CardStatus, string> = {
critical: "#ef4444",
@@ -50,6 +52,7 @@ export default function QuotaCard({
togglingActive,
}: QuotaCardProps) {
const isActive = connection.isActive ?? true;
const [costModalOpen, setCostModalOpen] = useState(false);
const quotas = quota?.quotas ?? EMPTY_QUOTAS;
const cardStatus = useMemo<CardStatus>(() => worstStatus(quotas), [quotas]);
const tierMeta = useMemo(
@@ -63,6 +66,17 @@ export default function QuotaCard({
() => resolvePlanValue(quota?.plan ?? null, connection.providerSpecificData ?? null),
[quota?.plan, connection.providerSpecificData]
);
const accountLabel = useMemo(
() =>
pickDisplayValue(
[connection.name, connection.displayName, connection.email],
emailsVisible,
connection.provider
) ||
connection.id ||
connection.provider,
[connection, emailsVisible]
);
const overrides = (connection.quotaWindowThresholds as Record<string, number> | null) || null;
const hasOverrides = !!overrides && Object.keys(overrides).length > 0;
@@ -96,9 +110,17 @@ export default function QuotaCard({
hasStaleData={hasStaleData}
onRefresh={onRefresh}
onOpenCutoff={onOpenCutoff}
onOpenCost={() => setCostModalOpen(true)}
canEditCutoff={canEditCutoff}
hasCutoffOverrides={hasOverrides}
/>
<ProviderUsdCostModal
isOpen={costModalOpen}
onClose={() => setCostModalOpen(false)}
connection={connection}
providerLabel={providerLabel}
accountLabel={accountLabel}
/>
</Card>
);
}

View File

@@ -30,6 +30,7 @@ interface Props {
hasStaleData: boolean;
onRefresh: () => void;
onOpenCutoff: () => void;
onOpenCost: () => void;
canEditCutoff: boolean;
hasCutoffOverrides: boolean;
}
@@ -115,6 +116,7 @@ export default function QuotaCardExpanded({
hasStaleData,
onRefresh,
onOpenCutoff,
onOpenCost,
canEditCutoff,
hasCutoffOverrides,
}: Props) {
@@ -189,6 +191,17 @@ export default function QuotaCardExpanded({
<span className="material-symbols-outlined text-[12px]">tune</span>
{tr("editCutoffs", "Edit cutoffs")}
</button>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onOpenCost();
}}
className="inline-flex items-center gap-1 text-[11px] font-medium px-2 py-1 rounded-md border border-border bg-bg-subtle hover:bg-black/[0.04] dark:hover:bg-white/[0.04] cursor-pointer"
>
<span className="material-symbols-outlined text-[12px]">bar_chart</span>
USD Cost
</button>
<button
type="button"
disabled={loading}

View File

@@ -0,0 +1,26 @@
import { NextResponse } from "next/server";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { getProviderWindowCostBreakdown } from "@/lib/usage/providerWindowCosts";
const PROVIDER_RE = /^[a-z0-9._-]{1,80}$/i;
export async function GET(request: Request) {
const authError = await requireManagementAuth(request);
if (authError) return authError;
try {
const { searchParams } = new URL(request.url);
const provider = (searchParams.get("provider") || "").trim().toLowerCase();
const connectionId = (searchParams.get("connectionId") || "").trim() || null;
if (!provider || !PROVIDER_RE.test(provider)) {
return NextResponse.json({ error: "provider query param is required" }, { status: 400 });
}
const breakdown = await getProviderWindowCostBreakdown({ provider, connectionId });
return NextResponse.json(breakdown);
} catch (error) {
console.error("[API] GET /api/usage/provider-window-costs error:", error);
return NextResponse.json({ error: "Failed to fetch provider USD costs" }, { status: 500 });
}
}

View File

@@ -36,6 +36,17 @@ interface QuotaSnapshotObservationRow {
remainingPercentage: number | null;
}
interface QuotaSnapshotWindowRow {
nextResetAt: string | null;
remainingPercentage: number | null;
createdAt: string | null;
}
export interface ProviderQuotaWindowStart {
windowStartIso: string;
source: "recorded_reset_event" | "observed_snapshot_reset";
}
function toNumberOrNull(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value === "string" && value.trim()) {
@@ -55,6 +66,17 @@ function usedPercent(remainingPercentage: number | null): number | null {
return remaining === null ? null : Math.max(0, Math.min(100, 100 - remaining));
}
function isResetDrop(
previousUsedPercentage: number | null,
currentUsedPercentage: number
): boolean {
if (previousUsedPercentage === null) return false;
const droppedToResetFloor =
currentUsedPercentage <= 1 && previousUsedPercentage > currentUsedPercentage;
const significantDrop = previousUsedPercentage - currentUsedPercentage >= 5;
return droppedToResetFloor || significantDrop;
}
function parseResetIso(value: string | null): string | null {
if (!value) return null;
const parsed = Date.parse(value);
@@ -127,12 +149,22 @@ export function recordProviderQuotaResetEventIfChanged(input: ResetEventInput):
const previousResetMs = Date.parse(previousResetIso);
const currentResetMs = Date.parse(currentResetIso);
if (!Number.isFinite(previousResetMs) || !Number.isFinite(currentResetMs)) return;
if (currentResetMs <= previousResetMs) return;
if (resetDay(previousResetIso) === resetDay(currentResetIso)) return;
const previousRemaining = clampPercent(toNumberOrNull(previous?.remainingPercentage));
const currentRemaining = clampPercent(toNumberOrNull(input.currentRemainingPercentage));
const observedAt = parseResetIso(input.observedAt ?? null) ?? new Date().toISOString();
const previousUsed = usedPercent(previousRemaining);
const currentUsed = usedPercent(currentRemaining);
const resetMovedForward =
currentResetMs > previousResetMs && resetDay(previousResetIso) !== resetDay(currentResetIso);
const resetObservedWithinSameResetAt =
resetDay(previousResetIso) === resetDay(currentResetIso) &&
currentUsed !== null &&
isResetDrop(previousUsed, currentUsed);
if (!resetMovedForward && !resetObservedWithinSameResetAt) return;
const windowStartedAt = resetMovedForward ? previousResetIso : observedAt;
try {
const db = getDbInstance() as unknown as DbLike;
@@ -148,13 +180,13 @@ export function recordProviderQuotaResetEventIfChanged(input: ResetEventInput):
input.provider,
input.connectionId,
input.windowKey,
previousResetIso,
windowStartedAt,
currentResetIso,
observedAt,
previousRemaining,
currentRemaining,
usedPercent(previousRemaining),
usedPercent(currentRemaining),
previousUsed,
currentUsed,
null
);
} catch (error: unknown) {
@@ -163,7 +195,7 @@ export function recordProviderQuotaResetEventIfChanged(input: ResetEventInput):
}
}
export function getProviderQuotaWindowStartIso(
function getRecordedQuotaWindowStartIso(
connectionId: string,
targetResetAtIso: string,
nowMs = Date.now()
@@ -204,3 +236,99 @@ export function getProviderQuotaWindowStartIso(
throw error;
}
}
function getObservedQuotaWindowStartIso(
connectionId: string,
targetResetAtIso: string,
nowMs = Date.now()
): { windowStartIso: string; resetDrop: boolean } | null {
if (!connectionId || !targetResetAtIso) return null;
const targetDay = resetDay(targetResetAtIso);
if (!targetDay) return null;
const db = getDbInstance() as unknown as DbLike;
const nowIso = new Date(nowMs).toISOString();
try {
const rows = db
.prepare<QuotaSnapshotWindowRow>(
`
SELECT
next_reset_at as nextResetAt,
remaining_percentage as remainingPercentage,
created_at as createdAt
FROM quota_snapshots
WHERE connection_id = @connectionId
AND LOWER(window_key) LIKE '%weekly%'
AND LOWER(window_key) NOT LIKE '%sonnet%'
AND created_at <= @nowIso
ORDER BY created_at ASC, id ASC
`
)
.all({ connectionId, nowIso });
let firstObservedIso: string | null = null;
let resetDropIso: string | null = null;
let previousUsedPercentage: number | null = null;
for (const row of rows) {
const createdIso = parseResetIso(row.createdAt);
if (!createdIso || resetDay(row.nextResetAt) !== targetDay) continue;
if (!firstObservedIso) firstObservedIso = createdIso;
const currentUsedPercentage = usedPercent(clampPercent(row.remainingPercentage));
if (currentUsedPercentage !== null) {
if (isResetDrop(previousUsedPercentage, currentUsedPercentage)) {
resetDropIso = createdIso;
}
previousUsedPercentage = currentUsedPercentage;
}
}
if (resetDropIso) return { windowStartIso: resetDropIso, resetDrop: true };
if (firstObservedIso) return { windowStartIso: firstObservedIso, resetDrop: false };
return null;
} catch (error: unknown) {
if (error instanceof Error && error.message.includes("no such table")) return null;
throw error;
}
}
export function getProviderQuotaWindowStart(
connectionId: string,
targetResetAtIso: string,
nowMs = Date.now()
): ProviderQuotaWindowStart | null {
const recordedIso = getRecordedQuotaWindowStartIso(connectionId, targetResetAtIso, nowMs);
const observed = getObservedQuotaWindowStartIso(connectionId, targetResetAtIso, nowMs);
if (!recordedIso && !observed) return null;
if (!recordedIso && observed) {
return { windowStartIso: observed.windowStartIso, source: "observed_snapshot_reset" };
}
if (recordedIso && !observed) {
return { windowStartIso: recordedIso, source: "recorded_reset_event" };
}
const recordedMs = Date.parse(recordedIso!);
const observedMs = Date.parse(observed!.windowStartIso);
if (
observed!.resetDrop &&
Number.isFinite(recordedMs) &&
Number.isFinite(observedMs) &&
observedMs > recordedMs
) {
return { windowStartIso: observed!.windowStartIso, source: "observed_snapshot_reset" };
}
return { windowStartIso: recordedIso!, source: "recorded_reset_event" };
}
export function getProviderQuotaWindowStartIso(
connectionId: string,
targetResetAtIso: string,
nowMs = Date.now()
): string | null {
return getProviderQuotaWindowStart(connectionId, targetResetAtIso, nowMs)?.windowStartIso ?? null;
}

View File

@@ -11,6 +11,7 @@ const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
export interface ApiKeyUsageLimitMetadata {
id: string;
allowedConnections?: string[] | null;
preferredProvider?: string | null;
usageLimitEnabled?: boolean;
dailyUsageLimitUsd?: number | null;
weeklyUsageLimitUsd?: number | null;
@@ -51,6 +52,7 @@ interface UsageCostRow {
interface WeeklyResetCandidate {
connectionId: string;
provider: string;
resetAtIso: string;
observedWindowStartIso: string | null;
}
@@ -104,6 +106,11 @@ function formatUsagePercent(percent: number | null): string {
return `${Math.round(percent)}%`;
}
function formatLeftPercent(percent: number | null): string {
if (percent === null || !Number.isFinite(percent)) return "Unavailable";
return `${Math.round(100 - clampPercent(percent))}% left`;
}
function formatResetIn(resetAt: string | null, now = Date.now()): string {
if (!resetAt) return "unknown";
const resetMs = Date.parse(resetAt);
@@ -113,12 +120,15 @@ function formatResetIn(resetAt: string | null, now = Date.now()): string {
if (deltaMs <= 0) return "now";
const minuteMs = 60_000;
const hourMs = 60 * minuteMs;
const dayMs = 24 * hourMs;
const totalMinutes = Math.max(1, Math.ceil(deltaMs / minuteMs));
const dayMinutes = 24 * 60;
const days = Math.floor(totalMinutes / dayMinutes);
const hours = Math.floor((totalMinutes % dayMinutes) / 60);
const minutes = totalMinutes % 60;
if (deltaMs < hourMs) return `${Math.max(1, Math.ceil(deltaMs / minuteMs))}m`;
if (deltaMs < dayMs) return `${Math.max(1, Math.ceil(deltaMs / hourMs))}h`;
return `${Math.max(1, Math.ceil(deltaMs / dayMs))}d`;
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
if (hours > 0) return `${hours}h ${minutes}m`;
return `${minutes}m`;
}
function resetDay(value: string | null): string | null {
@@ -158,6 +168,13 @@ function normalizeQuotaName(value: string): string {
.trim();
}
function normalizeProvider(value: unknown): string {
if (typeof value !== "string") return "";
const normalized = value.trim().toLowerCase();
if (normalized === "cc" || normalized === "claude-code") return "claude";
return normalized;
}
function findWeeklyQuotaResetAt(quotas: unknown, nowMs: number): string | null {
const quotaEntries: Array<[string, Record<string, unknown>]> = [];
if (Array.isArray(quotas)) {
@@ -307,7 +324,7 @@ async function getProviderWeeklyWindow(
if (allowedConnections.length > 0) {
for (const connectionId of allowedConnections) {
const connection = connectionFromValue(await deps.getProviderConnectionById(connectionId));
if (!connection || connection.provider.toLowerCase() !== "claude") continue;
if (!connection) continue;
const resetAt = findWeeklyQuotaResetAt(
deps.getProviderLimitsCache(connection.id)?.quotas,
nowMs
@@ -315,6 +332,7 @@ async function getProviderWeeklyWindow(
if (resetAt) {
resetCandidates.push({
connectionId: connection.id,
provider: connection.provider,
resetAtIso: resetAt,
observedWindowStartIso: getWeeklyWindowStartIso(connection.id, resetAt, nowMs),
});
@@ -325,11 +343,12 @@ async function getProviderWeeklyWindow(
const connections = await deps.getProviderConnections({ isActive: true });
for (const rawConnection of connections) {
const connection = connectionFromValue(rawConnection);
if (!connection || connection.provider.toLowerCase() !== "claude") continue;
if (!connection) continue;
const resetAt = findWeeklyQuotaResetAt(caches[connection.id]?.quotas, nowMs);
if (resetAt) {
resetCandidates.push({
connectionId: connection.id,
provider: connection.provider,
resetAtIso: resetAt,
observedWindowStartIso: getWeeklyWindowStartIso(connection.id, resetAt, nowMs),
});
@@ -337,8 +356,15 @@ async function getProviderWeeklyWindow(
}
}
const preferredProvider = normalizeProvider(metadata.preferredProvider);
const scopedCandidates = preferredProvider
? resetCandidates.filter(
(candidate) => normalizeProvider(candidate.provider) === preferredProvider
)
: [];
const candidates = scopedCandidates.length > 0 ? scopedCandidates : resetCandidates;
const selected =
resetCandidates
candidates
.sort((left, right) => Date.parse(left.resetAtIso) - Date.parse(right.resetAtIso))
.at(0) ?? null;
return {
@@ -442,36 +468,64 @@ export function buildApiKeyUsageLimitText(
now = Date.now()
): string {
return [
"Cota diaria",
"Daily quota",
formatUsd(status.dailyLimitUsd),
"Gasto diario",
"Daily spent",
formatUsd(status.dailySpentUsd),
"Uso diario",
"Daily used",
formatUsagePercent(getUsagePercent(status.dailySpentUsd, status.dailyLimitUsd)),
`Resets in ${formatResetIn(status.dailyResetAtIso, now)}`,
"",
"Cota semanal",
"Weekly quota",
formatUsd(status.weeklyLimitUsd),
"Gasto semanal",
"Weekly spent",
formatUsd(status.weeklySpentUsd),
"Uso semanal",
"Weekly used",
formatUsagePercent(getUsagePercent(status.weeklySpentUsd, status.weeklyLimitUsd)),
`Resets in ${formatResetIn(status.weeklyResetAtIso, now)}`,
].join("\n");
}
function buildUsageLimitExceededMessage(status: ApiKeyUsageLimitStatus, now = Date.now()): string {
export function buildApiKeyUsageLimitPercentText(
status: ApiKeyUsageLimitStatus,
now = Date.now()
): string {
return [
"Daily",
formatLeftPercent(getUsagePercent(status.dailySpentUsd, status.dailyLimitUsd)),
`⏱ reset in ${formatResetIn(status.dailyResetAtIso, now)}`,
"",
"Weekly",
formatLeftPercent(getUsagePercent(status.weeklySpentUsd, status.weeklyLimitUsd)),
`⏱ reset in ${formatResetIn(status.weeklyResetAtIso, now)}`,
].join("\n");
}
function buildUsageLimitExceededMessage(
status: ApiKeyUsageLimitStatus,
now = Date.now(),
options: { showUsd?: boolean } = {}
): string {
const showUsd = options.showUsd !== false;
if (status.dailyExceeded && status.dailyLimitUsd !== null) {
const percent = formatUsagePercent(getUsagePercent(status.dailySpentUsd, status.dailyLimitUsd));
if (!showUsd) {
return `This API key reached its daily usage quota (${percent}). Resets in ${formatResetIn(status.dailyResetAtIso, now)}. Choose another allowed model after reset.`;
}
return `This API key reached its daily USD usage quota (${formatUsd(status.dailySpentUsd)} of ${formatUsd(status.dailyLimitUsd)}, ${percent}). Resets in ${formatResetIn(status.dailyResetAtIso, now)}. Choose another allowed model after reset.`;
}
if (status.weeklyExceeded && status.weeklyLimitUsd !== null) {
const percent = formatUsagePercent(
getUsagePercent(status.weeklySpentUsd, status.weeklyLimitUsd)
);
if (!showUsd) {
return `This API key reached its weekly usage quota (${percent}). Resets in ${formatResetIn(status.weeklyResetAtIso, now)}. Choose another allowed model after reset.`;
}
return `This API key reached its weekly USD usage quota (${formatUsd(status.weeklySpentUsd)} of ${formatUsd(status.weeklyLimitUsd)}, ${percent}). Resets in ${formatResetIn(status.weeklyResetAtIso, now)}. Choose another allowed model after reset.`;
}
return "This API key reached its USD usage quota. Choose another allowed model or wait for quota reset.";
return showUsd
? "This API key reached its USD usage quota. Choose another allowed model or wait for quota reset."
: "This API key reached its usage quota. Choose another allowed model or wait for quota reset.";
}
function isAnthropicMessagesRequest(request: Request): boolean {
@@ -486,9 +540,10 @@ function isAnthropicMessagesRequest(request: Request): boolean {
export function buildApiKeyUsageLimitRejection(
request: Request,
status: ApiKeyUsageLimitStatus,
now = Date.now()
now = Date.now(),
options: { showUsd?: boolean } = {}
): Response {
const message = sanitizeErrorMessage(buildUsageLimitExceededMessage(status, now));
const message = sanitizeErrorMessage(buildUsageLimitExceededMessage(status, now, options));
if (isAnthropicMessagesRequest(request)) {
return new Response(
JSON.stringify({
@@ -517,5 +572,7 @@ export async function buildApiKeyUsageLimitPolicyRejection(
): Promise<Response | null> {
const status = await getApiKeyUsageLimitStatus(metadata);
if (!status.enabled || (!status.dailyExceeded && !status.weeklyExceeded)) return null;
return buildApiKeyUsageLimitRejection(request, status);
return buildApiKeyUsageLimitRejection(request, status, Date.now(), {
showUsd: false,
});
}

View File

@@ -1,6 +1,6 @@
import type { ProviderLimitsCacheEntry } from "@/lib/db/providerLimits";
import {
buildApiKeyUsageLimitText,
buildApiKeyUsageLimitPercentText,
type ApiKeyUsageLimitStatus,
} from "@/lib/usage/apiKeyUsageLimits";
import { buildErrorBody } from "@omniroute/open-sse/utils/error";
@@ -17,6 +17,7 @@ interface UsageCommandApiKeyMetadata {
id: string;
name?: string;
allowedConnections?: string[] | null;
preferredProvider?: string | null;
allowUsageCommand?: boolean;
usageLimitEnabled?: boolean;
dailyUsageLimitUsd?: number | null;
@@ -27,6 +28,7 @@ interface ProviderConnectionLike {
id: string;
provider: string;
isActive?: boolean;
quotaWindowThresholds?: Record<string, number> | null;
}
interface UsageSnapshot {
@@ -34,6 +36,7 @@ interface UsageSnapshot {
provider: string;
plan: unknown;
quotas: JsonRecord;
quotaWindowThresholds?: Record<string, number> | null;
}
interface UsageCommandSelection {
@@ -41,6 +44,11 @@ interface UsageCommandSelection {
preferredConnectionId?: string | null;
}
interface UsageCommandQuotaPolicy {
defaultThresholdPercent: number;
providerWindowDefaults: Record<string, Record<string, number>>;
}
export interface InternalUsageCommandDeps {
now?: () => number;
isValidApiKey?: (apiKey: string) => Promise<boolean>;
@@ -53,6 +61,7 @@ export interface InternalUsageCommandDeps {
metadata: UsageCommandApiKeyMetadata,
deps?: { now?: () => number }
) => Promise<ApiKeyUsageLimitStatus>;
getQuotaPolicy?: () => Promise<UsageCommandQuotaPolicy>;
}
type RequiredDeps = Required<InternalUsageCommandDeps>;
@@ -84,6 +93,19 @@ async function normalizeDeps(deps: InternalUsageCommandDeps = {}): Promise<Requi
deps.getAllProviderLimitsCache ?? providerLimits!.getAllProviderLimitsCache,
getApiKeyUsageLimitStatus:
deps.getApiKeyUsageLimitStatus ?? usageLimits!.getApiKeyUsageLimitStatus,
getQuotaPolicy: deps.getQuotaPolicy ?? getDefaultUsageCommandQuotaPolicy,
};
}
async function getDefaultUsageCommandQuotaPolicy(): Promise<UsageCommandQuotaPolicy> {
const [{ getCachedSettings }, { resolveResilienceSettings }] = await Promise.all([
import("@/lib/localDb"),
import("@/lib/resilience/settings"),
]);
const resilience = resolveResilienceSettings(await getCachedSettings());
return {
defaultThresholdPercent: resilience.quotaPreflight.defaultThresholdPercent,
providerWindowDefaults: resilience.quotaPreflight.providerWindowDefaults,
};
}
@@ -197,12 +219,29 @@ export function isInternalUsageCommand(text: string | null | undefined): boolean
return typeof text === "string" && text.trim() === INTERNAL_USAGE_COMMAND;
}
function readThresholdMap(value: unknown): Record<string, number> | null {
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
const out: Record<string, number> = {};
for (const [key, raw] of Object.entries(value as Record<string, unknown>)) {
const numeric = Number(raw);
if (key && Number.isFinite(numeric) && numeric >= 0 && numeric <= 100) {
out[key] = numeric;
}
}
return Object.keys(out).length > 0 ? out : null;
}
function connectionFromValue(value: unknown): ProviderConnectionLike | null {
if (!isRecord(value)) return null;
const id = typeof value.id === "string" ? value.id : "";
const provider = typeof value.provider === "string" ? value.provider : "";
if (!id || !provider || value.isActive === false) return null;
return { id, provider, isActive: value.isActive === true };
return {
id,
provider,
isActive: value.isActive === true,
quotaWindowThresholds: readThresholdMap(value.quotaWindowThresholds),
};
}
function snapshotFromConnection(
@@ -215,6 +254,7 @@ function snapshotFromConnection(
provider: connection.provider,
plan: cache.plan,
quotas: cache.quotas,
quotaWindowThresholds: connection.quotaWindowThresholds ?? null,
};
}
@@ -259,27 +299,35 @@ function normalizeQuotaKey(key: string): string {
.trim();
}
function findQuota(quotas: JsonRecord, kind: "session" | "weekly" | "weekly-sonnet") {
interface QuotaMatch {
key: string;
quota: JsonRecord;
}
function findQuota(
quotas: JsonRecord,
kind: "session" | "weekly" | "weekly-sonnet"
): QuotaMatch | null {
const entries = Object.entries(quotas).filter(([, value]) => isRecord(value));
for (const [key, value] of entries) {
const normalized = normalizeQuotaKey(key);
if (kind === "session" && (normalized.includes("session") || normalized.includes("5h"))) {
return value as JsonRecord;
return { key, quota: value as JsonRecord };
}
if (
kind === "weekly-sonnet" &&
normalized.includes("weekly") &&
normalized.includes("sonnet")
) {
return value as JsonRecord;
return { key, quota: value as JsonRecord };
}
if (
kind === "weekly" &&
(normalized === "weekly" || normalized.includes("weekly") || normalized.includes("7d")) &&
!normalized.includes("sonnet")
) {
return value as JsonRecord;
return { key, quota: value as JsonRecord };
}
}
@@ -320,9 +368,9 @@ function getResetAt(quota: JsonRecord | null): string | null {
return typeof quota.resetAt === "string" && quota.resetAt.trim() ? quota.resetAt : null;
}
function formatPercent(percent: number | null): string {
function formatLeftPercent(percent: number | null): string {
if (percent === null || !Number.isFinite(percent)) return "Unavailable";
return `${Math.round(percent)}%`;
return `${Math.round(Math.max(0, Math.min(100, percent)))}% left`;
}
export function formatResetIn(resetAt: string | null, now = Date.now()): string {
@@ -334,12 +382,15 @@ export function formatResetIn(resetAt: string | null, now = Date.now()): string
if (deltaMs <= 0) return "now";
const minuteMs = 60_000;
const hourMs = 60 * minuteMs;
const dayMs = 24 * hourMs;
const totalMinutes = Math.max(1, Math.ceil(deltaMs / minuteMs));
const dayMinutes = 24 * 60;
const days = Math.floor(totalMinutes / dayMinutes);
const hours = Math.floor((totalMinutes % dayMinutes) / 60);
const minutes = totalMinutes % 60;
if (deltaMs < hourMs) return `${Math.max(1, Math.ceil(deltaMs / minuteMs))}m`;
if (deltaMs < dayMs) return `${Math.max(1, Math.ceil(deltaMs / hourMs))}h`;
return `${Math.max(1, Math.ceil(deltaMs / dayMs))}d`;
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
if (hours > 0) return `${hours}h ${minutes}m`;
return `${minutes}m`;
}
function formatPlan(plan: unknown): string {
@@ -379,6 +430,62 @@ function normalizeProviderId(provider: string | null | undefined): string | null
return normalized;
}
function quotaWindowLookupNames(provider: string, windowName: string): string[] {
const names = [windowName];
const lower = windowName.toLowerCase();
if (lower !== windowName) names.push(lower);
const normalized = normalizeQuotaKey(windowName);
if (normalized.includes("session") || normalized.includes("5h")) {
names.push("session", "session (5h)");
}
if (normalized.includes("weekly") || normalized.includes("7d")) {
if (normalized.includes("sonnet")) {
names.push("weekly sonnet", "weekly sonnet (7d)");
} else {
names.push("weekly", "weekly (7d)");
}
}
if (provider === "codex" && (normalized.includes("monthly") || normalized.includes("30d"))) {
names.push("monthly");
}
return [...new Set(names)];
}
function resolveQuotaCutoffPercent(
snapshot: UsageSnapshot,
windowName: string,
policy: UsageCommandQuotaPolicy
): number {
const provider = normalizeProviderId(snapshot.provider) ?? snapshot.provider;
const providerDefaults =
policy.providerWindowDefaults[snapshot.provider] ||
policy.providerWindowDefaults[provider] ||
{};
const overrides = snapshot.quotaWindowThresholds ?? {};
for (const lookupName of quotaWindowLookupNames(provider, windowName)) {
const override = overrides[lookupName];
if (typeof override === "number") return override;
const providerDefault = providerDefaults[lookupName];
if (typeof providerDefault === "number") return providerDefault;
}
return policy.defaultThresholdPercent;
}
function effectiveRemainingPercent(
realRemaining: number | null,
cutoffPercent: number
): number | null {
if (realRemaining === null || !Number.isFinite(realRemaining)) return null;
const remaining = Math.max(0, Math.min(100, realRemaining));
const cutoff = Math.max(0, Math.min(99, cutoffPercent));
if (remaining <= cutoff) return 0;
return ((remaining - cutoff) / (100 - cutoff)) * 100;
}
function selectUsageSnapshot(
snapshots: UsageSnapshot[],
selection: UsageCommandSelection = {}
@@ -399,10 +506,23 @@ function selectUsageSnapshot(
return selectBestUsageSnapshot(snapshots);
}
function appendQuotaBlock(lines: string[], label: string, quota: JsonRecord | null, now: number) {
function appendQuotaBlock(
lines: string[],
label: string,
match: QuotaMatch | null,
snapshot: UsageSnapshot,
policy: UsageCommandQuotaPolicy,
now: number
) {
lines.push(label);
lines.push(formatPercent(getQuotaUsedPercent(quota)));
lines.push(`Resets in ${formatResetIn(getResetAt(quota), now)}`);
const usedPercent = getQuotaUsedPercent(match?.quota ?? null);
const realRemaining =
usedPercent === null || !Number.isFinite(usedPercent)
? null
: 100 - Math.max(0, Math.min(100, usedPercent));
const cutoff = match ? resolveQuotaCutoffPercent(snapshot, match.key, policy) : 0;
lines.push(formatLeftPercent(effectiveRemainingPercent(realRemaining, cutoff)));
lines.push(`⏱ reset in ${formatResetIn(getResetAt(match?.quota ?? null), now)}`);
}
export async function buildUsageCommandText(
@@ -411,11 +531,17 @@ export async function buildUsageCommandText(
selection: UsageCommandSelection = {}
): Promise<string> {
const resolvedDeps = await normalizeDeps(deps);
const sections: string[] = [];
if (metadata.usageLimitEnabled === true) {
return buildApiKeyUsageLimitText(
await resolvedDeps.getApiKeyUsageLimitStatus(metadata, { now: resolvedDeps.now }),
resolvedDeps.now()
);
const usageMetadata: UsageCommandApiKeyMetadata = {
...metadata,
preferredProvider: selection.preferredProvider ?? metadata.preferredProvider ?? null,
};
const status = await resolvedDeps.getApiKeyUsageLimitStatus(usageMetadata, {
now: resolvedDeps.now,
});
const now = resolvedDeps.now();
sections.push(["Personal quota", buildApiKeyUsageLimitPercentText(status, now)].join("\n"));
}
const snapshot = selectUsageSnapshot(
@@ -424,17 +550,18 @@ export async function buildUsageCommandText(
);
if (!snapshot) {
return ["Plan", "Unavailable", "", "Usage", "No cached usage data available."].join("\n");
sections.push(["Provider quota", "No cached usage data available."].join("\n"));
return sections.join("\n\n");
}
const now = resolvedDeps.now();
const lines = ["Plan", formatPlan(snapshot.plan), "", "Usage"];
appendQuotaBlock(lines, "Session (5hr)", findQuota(snapshot.quotas, "session"), now);
const policy = await resolvedDeps.getQuotaPolicy();
const lines = ["Provider quota"];
appendQuotaBlock(lines, "Session", findQuota(snapshot.quotas, "session"), snapshot, policy, now);
lines.push("");
appendQuotaBlock(lines, "Weekly (7 day)", findQuota(snapshot.quotas, "weekly"), now);
lines.push("");
appendQuotaBlock(lines, "Weekly Sonnet", findQuota(snapshot.quotas, "weekly-sonnet"), now);
return lines.join("\n");
appendQuotaBlock(lines, "Weekly", findQuota(snapshot.quotas, "weekly"), snapshot, policy, now);
sections.push(lines.join("\n"));
return sections.join("\n\n");
}
function getResponseModel(body: unknown): string {

View File

@@ -0,0 +1,571 @@
import { getCostSummary } from "@/domain/costRules";
import { getApiKeys } from "@/lib/db/apiKeys";
import { getDbInstance } from "@/lib/db/core";
import { getAllProviderLimitsCache, getProviderLimitsCache } from "@/lib/db/providerLimits";
import { getProviderQuotaWindowStart } from "@/lib/db/quotaResetEvents";
import { calculateCost } from "@/lib/usage/costCalculator";
const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
const RECORDED_COST_MATCH_TOLERANCE_MS = 30_000;
type JsonRecord = Record<string, unknown>;
interface UsageCostRow {
id: number;
apiKeyId: string | null;
apiKeyName: string | null;
provider: string;
model: string;
serviceTier: string;
promptTokens: number;
completionTokens: number;
cacheReadTokens: number;
cacheCreationTokens: number;
reasoningTokens: number;
totalTokens: number;
timestamp: string | null;
}
interface RecordedCostRow {
rowId: number;
apiKeyId: string;
timestamp: number;
cost: number;
}
interface ProviderWindowCostModelRow {
model: string;
provider: string;
serviceTier: string;
requests: number;
totalTokens: number;
costUsd: number;
}
export interface ProviderWindowCostBreakdownRow {
apiKeyKey: string;
apiKeyId: string | null;
apiKeyName: string;
requests: number;
promptTokens: number;
completionTokens: number;
totalTokens: number;
costUsd: number;
limitUsd: number | null;
limitPeriod: string | null;
limitUsedPercent: number | null;
budgetResetAt: string | null;
lastUsed: string | null;
models: ProviderWindowCostModelRow[];
}
interface ProviderWindowCostAggregateRow extends ProviderWindowCostBreakdownRow {
modelMap: Map<string, ProviderWindowCostModelRow>;
}
export interface ProviderWindowCostBreakdown {
provider: string;
connectionId: string | null;
windowStartAt: string;
windowResetAt: string | null;
windowSource: "provider_weekly_reset" | "fallback_rolling_7d";
windowStartSource:
| "recorded_reset_event"
| "observed_snapshot_reset"
| "inferred_from_reset_at"
| "fallback_rolling_7d";
quotaName: string | null;
quotaUsedPercent: number | null;
quotaRemainingPercent: number | null;
totalCostUsd: number;
estimatedFullQuotaUsd: number | null;
rows: ProviderWindowCostBreakdownRow[];
}
function toRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
}
function toNumber(value: unknown, fallback = 0): number {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : fallback;
}
return fallback;
}
function toString(value: unknown): string {
return typeof value === "string" ? value.trim() : "";
}
function roundUsd(value: number): number {
return Math.round(value * 1_000_000) / 1_000_000;
}
function roundPercent(value: number): number {
return Math.round(value * 10) / 10;
}
function parseResetAt(value: unknown, nowMs: number): number | null {
const resetAt = toString(value);
if (!resetAt) return null;
const parsed = Date.parse(resetAt);
if (!Number.isFinite(parsed) || parsed <= nowMs) return null;
return parsed;
}
function getProviderWindowStart(
connectionId: string | null,
resetMs: number,
nowMs: number
): { startMs: number; source: ProviderWindowCostBreakdown["windowStartSource"] } | null {
if (!connectionId) return null;
const resetIso = new Date(resetMs).toISOString();
const start = getProviderQuotaWindowStart(connectionId, resetIso, nowMs);
if (!start) return null;
const startMs = Date.parse(start.windowStartIso);
if (!Number.isFinite(startMs)) return null;
if (startMs > nowMs || startMs >= resetMs) return null;
return { startMs, source: start.source };
}
function getRemainingPercent(quota: JsonRecord): number | null {
const explicit = toNumber(quota.remainingPercentage, Number.NaN);
if (Number.isFinite(explicit)) return Math.max(0, Math.min(100, explicit));
const total = toNumber(quota.total, 0);
if (total <= 0) return null;
const remaining = toNumber(quota.remaining, Number.NaN);
if (Number.isFinite(remaining)) {
return Math.max(0, Math.min(100, (remaining / total) * 100));
}
const used = toNumber(quota.used, Number.NaN);
if (Number.isFinite(used)) {
return Math.max(0, Math.min(100, ((total - used) / total) * 100));
}
return null;
}
function scoreWeeklyQuota(name: string): number {
const normalized = name.trim().toLowerCase();
if (!normalized.includes("weekly") && !normalized.includes("7d")) return Number.NEGATIVE_INFINITY;
let score = 10;
if (normalized === "weekly" || /^weekly\s*\(/.test(normalized)) score += 100;
if (normalized.includes("7d") || normalized.includes("7 day")) score += 15;
if (normalized.includes("sonnet")) score -= 30;
if (/^(gpt|claude|o\d|gemini|opus|sonnet)\b/.test(normalized)) score -= 20;
return score;
}
function selectWeeklyWindow(
provider: string,
connectionId: string | null,
nowMs: number
): {
startMs: number;
resetMs: number | null;
source: ProviderWindowCostBreakdown["windowSource"];
quotaName: string | null;
quotaUsedPercent: number | null;
quotaRemainingPercent: number | null;
windowStartSource: ProviderWindowCostBreakdown["windowStartSource"];
} {
const cacheEntries = connectionId
? [[connectionId, getProviderLimitsCache(connectionId)] as const]
: Object.entries(getAllProviderLimitsCache());
let selected: {
score: number;
connectionId: string;
resetMs: number;
quotaName: string;
quotaUsedPercent: number | null;
quotaRemainingPercent: number | null;
} | null = null;
for (const [entryConnectionId, cache] of cacheEntries) {
const quotas = toRecord(cache?.quotas);
for (const [name, rawQuota] of Object.entries(quotas)) {
const score = scoreWeeklyQuota(name);
if (!Number.isFinite(score)) continue;
const quota = toRecord(rawQuota);
const resetMs = parseResetAt(quota.resetAt, nowMs);
if (resetMs === null) continue;
const remainingPercent = getRemainingPercent(quota);
const usedPercent =
remainingPercent === null ? null : Math.max(0, Math.min(100, 100 - remainingPercent));
if (
!selected ||
score > selected.score ||
(score === selected.score && resetMs < selected.resetMs)
) {
selected = {
score,
connectionId: entryConnectionId,
resetMs,
quotaName: name,
quotaUsedPercent: usedPercent,
quotaRemainingPercent: remainingPercent,
};
}
}
}
if (selected) {
const providerWindowStart = getProviderWindowStart(
selected.connectionId,
selected.resetMs,
nowMs
);
return {
startMs: providerWindowStart?.startMs ?? selected.resetMs - WEEK_MS,
resetMs: selected.resetMs,
source: "provider_weekly_reset",
windowStartSource: providerWindowStart?.source ?? "inferred_from_reset_at",
quotaName: selected.quotaName,
quotaUsedPercent: selected.quotaUsedPercent,
quotaRemainingPercent: selected.quotaRemainingPercent,
};
}
return {
startMs: nowMs - WEEK_MS,
resetMs: null,
source: "fallback_rolling_7d",
windowStartSource: "fallback_rolling_7d",
quotaName: null,
quotaUsedPercent: null,
quotaRemainingPercent: null,
};
}
function makeApiKeyKey(apiKeyId: string | null, apiKeyName: string | null): string {
if (apiKeyId) return `id:${apiKeyId}`;
if (apiKeyName) return `name:${apiKeyName}`;
return "unattributed";
}
async function getCurrentApiKeyNames(): Promise<Map<string, string>> {
const names = new Map<string, string>();
try {
const apiKeys = await getApiKeys();
for (const apiKey of apiKeys) {
if (typeof apiKey.id === "string" && typeof apiKey.name === "string") {
names.set(apiKey.id, apiKey.name);
}
}
} catch {
// Usage rows carry historical names, so current API key names are an enhancement only.
}
return names;
}
function uniqueApiKeyIds(rows: UsageCostRow[]): string[] {
return Array.from(
new Set(
rows
.map((row) => (typeof row.apiKeyId === "string" ? row.apiKeyId : ""))
.filter((value) => value.length > 0)
)
);
}
function appendNamedPlaceholders(
params: Record<string, unknown>,
prefix: string,
values: string[]
): string {
return values
.map((value, index) => {
const key = `${prefix}${index}`;
params[key] = value;
return `@${key}`;
})
.join(", ");
}
function getRecordedCostsByApiKey(
apiKeyIds: string[],
sinceMs: number,
untilMs: number
): Map<string, RecordedCostRow[]> {
if (apiKeyIds.length === 0) return new Map();
try {
const params: Record<string, unknown> = {
sinceMs: Math.max(0, sinceMs - RECORDED_COST_MATCH_TOLERANCE_MS),
untilMs: untilMs + RECORDED_COST_MATCH_TOLERANCE_MS,
};
const placeholders = appendNamedPlaceholders(params, "apiKey", apiKeyIds);
const rows = getDbInstance()
.prepare<RecordedCostRow>(
`
SELECT
id as rowId,
api_key_id as apiKeyId,
timestamp,
cost
FROM domain_cost_history
WHERE api_key_id IN (${placeholders})
AND timestamp >= @sinceMs
AND timestamp <= @untilMs
ORDER BY api_key_id ASC, timestamp ASC, rowid ASC
`
)
.all(params);
const byApiKey = new Map<string, RecordedCostRow[]>();
for (const row of rows) {
if (!row.apiKeyId || !Number.isFinite(row.timestamp) || !Number.isFinite(row.cost)) {
continue;
}
const list = byApiKey.get(row.apiKeyId) ?? [];
list.push(row);
byApiKey.set(row.apiKeyId, list);
}
return byApiKey;
} catch {
return new Map();
}
}
function findClosestRecordedCost(
candidates: RecordedCostRow[] | undefined,
timestampMs: number,
usedRecordedRows: Set<number>
): RecordedCostRow | null {
if (!candidates?.length || !Number.isFinite(timestampMs)) return null;
let best: RecordedCostRow | null = null;
let bestDelta = Number.POSITIVE_INFINITY;
for (const candidate of candidates) {
if (usedRecordedRows.has(candidate.rowId)) continue;
const delta = Math.abs(candidate.timestamp - timestampMs);
if (delta > RECORDED_COST_MATCH_TOLERANCE_MS) {
if (candidate.timestamp > timestampMs + RECORDED_COST_MATCH_TOLERANCE_MS) break;
continue;
}
if (delta < bestDelta) {
best = candidate;
bestDelta = delta;
}
}
if (best) usedRecordedRows.add(best.rowId);
return best;
}
async function getUsageRowCostUsd(
row: UsageCostRow,
recordedCostsByApiKey: Map<string, RecordedCostRow[]>,
usedRecordedRows: Set<number>
): Promise<number> {
const usageTimestampMs = Date.parse(row.timestamp ?? "");
const recordedCost = findClosestRecordedCost(
row.apiKeyId ? recordedCostsByApiKey.get(row.apiKeyId) : undefined,
usageTimestampMs,
usedRecordedRows
);
if (recordedCost) return Math.max(0, toNumber(recordedCost.cost));
return calculateCost(
row.provider,
row.model,
{
input: toNumber(row.promptTokens),
output: toNumber(row.completionTokens),
cacheRead: toNumber(row.cacheReadTokens),
cacheCreation: toNumber(row.cacheCreationTokens),
reasoning: toNumber(row.reasoningTokens),
},
{ serviceTier: row.serviceTier }
);
}
export async function getProviderWindowCostBreakdown({
provider,
connectionId = null,
now = Date.now(),
}: {
provider: string;
connectionId?: string | null;
now?: number;
}): Promise<ProviderWindowCostBreakdown> {
const providerKey = provider.trim().toLowerCase();
const nowMs = Number.isFinite(now) ? now : Date.now();
const window = selectWeeklyWindow(providerKey, connectionId, nowMs);
const windowStartAt = new Date(window.startMs).toISOString();
const windowResetAt = window.resetMs ? new Date(window.resetMs).toISOString() : null;
const nowIso = new Date(nowMs).toISOString();
const where = [
"LOWER(provider) = @provider",
"timestamp >= @since",
"timestamp <= @nowIso",
"COALESCE(success, 1) = 1",
];
const params: Record<string, unknown> = {
provider: providerKey,
since: windowStartAt,
nowIso,
};
if (windowResetAt) {
where.push("timestamp < @resetAt");
params.resetAt = windowResetAt;
}
if (connectionId) {
where.push("connection_id = @connectionId");
params.connectionId = connectionId;
}
const usageRows = getDbInstance()
.prepare<UsageCostRow>(
`
SELECT
id,
NULLIF(api_key_id, '') as apiKeyId,
NULLIF(api_key_name, '') as apiKeyName,
LOWER(provider) as provider,
LOWER(model) as model,
COALESCE(NULLIF(service_tier, ''), 'standard') as serviceTier,
COALESCE(tokens_input, 0) as promptTokens,
COALESCE(tokens_output, 0) as completionTokens,
COALESCE(tokens_cache_read, 0) as cacheReadTokens,
COALESCE(tokens_cache_creation, 0) as cacheCreationTokens,
COALESCE(tokens_reasoning, 0) as reasoningTokens,
COALESCE(tokens_input + tokens_output, 0) as totalTokens,
timestamp
FROM usage_history
WHERE ${where.join(" AND ")}
ORDER BY timestamp ASC, id ASC
`
)
.all(params);
const currentApiKeyNames = await getCurrentApiKeyNames();
const recordedCostsByApiKey = getRecordedCostsByApiKey(
uniqueApiKeyIds(usageRows),
window.startMs,
nowMs
);
const usedRecordedRows = new Set<number>();
const byApiKey = new Map<string, ProviderWindowCostAggregateRow>();
for (const row of usageRows) {
const apiKeyId = row.apiKeyId || null;
const apiKeyName = row.apiKeyName || null;
const apiKeyKey = makeApiKeyKey(apiKeyId, apiKeyName);
const displayName =
(apiKeyId ? currentApiKeyNames.get(apiKeyId) : null) ||
apiKeyName ||
apiKeyId ||
"Unattributed";
const costUsd = roundUsd(
await getUsageRowCostUsd(row, recordedCostsByApiKey, usedRecordedRows)
);
let aggregate = byApiKey.get(apiKeyKey);
if (!aggregate) {
let limitUsd: number | null = null;
let limitPeriod: string | null = null;
let budgetResetAt: string | null = null;
if (apiKeyId) {
const summary = getCostSummary(apiKeyId);
if (summary.activeLimitUsd > 0) {
limitUsd = summary.activeLimitUsd;
limitPeriod = summary.resetInterval;
budgetResetAt =
typeof summary.nextResetAt === "number" && Number.isFinite(summary.nextResetAt)
? new Date(summary.nextResetAt).toISOString()
: null;
}
}
aggregate = {
apiKeyKey,
apiKeyId,
apiKeyName: displayName,
requests: 0,
promptTokens: 0,
completionTokens: 0,
totalTokens: 0,
costUsd: 0,
limitUsd,
limitPeriod,
limitUsedPercent: null,
budgetResetAt,
lastUsed: null,
models: [],
modelMap: new Map(),
};
byApiKey.set(apiKeyKey, aggregate);
}
aggregate.requests += 1;
aggregate.promptTokens += toNumber(row.promptTokens);
aggregate.completionTokens += toNumber(row.completionTokens);
aggregate.totalTokens += toNumber(row.totalTokens);
aggregate.costUsd = roundUsd(aggregate.costUsd + costUsd);
if (!aggregate.lastUsed || (row.timestamp && row.timestamp > aggregate.lastUsed)) {
aggregate.lastUsed = row.timestamp || aggregate.lastUsed;
}
const modelKey = `${row.provider}\0${row.model}\0${row.serviceTier}`;
const model = aggregate.modelMap.get(modelKey) ?? {
model: row.model,
provider: row.provider,
serviceTier: row.serviceTier,
requests: 0,
totalTokens: 0,
costUsd: 0,
};
model.requests += 1;
model.totalTokens += toNumber(row.totalTokens);
model.costUsd = roundUsd(model.costUsd + costUsd);
aggregate.modelMap.set(modelKey, model);
}
const breakdownRows = Array.from(byApiKey.values())
.map((row) => {
const limitUsedPercent =
row.limitUsd && row.limitUsd > 0 ? roundPercent((row.costUsd / row.limitUsd) * 100) : null;
const models = Array.from(row.modelMap.values())
.map((model) => ({ ...model, costUsd: roundUsd(model.costUsd) }))
.sort((left, right) => right.costUsd - left.costUsd);
const { modelMap, ...publicRow } = row;
void modelMap;
return {
...publicRow,
costUsd: roundUsd(row.costUsd),
limitUsedPercent,
models,
};
})
.sort((left, right) => right.costUsd - left.costUsd);
const totalCostUsd = roundUsd(breakdownRows.reduce((sum, row) => sum + row.costUsd, 0));
const estimatedFullQuotaUsd =
totalCostUsd > 0 && window.quotaUsedPercent && window.quotaUsedPercent > 0
? roundUsd(totalCostUsd / (window.quotaUsedPercent / 100))
: null;
return {
provider: providerKey,
connectionId,
windowStartAt,
windowResetAt,
windowSource: window.source,
windowStartSource: window.windowStartSource,
quotaName: window.quotaName,
quotaUsedPercent:
window.quotaUsedPercent === null ? null : roundPercent(window.quotaUsedPercent),
quotaRemainingPercent:
window.quotaRemainingPercent === null ? null : roundPercent(window.quotaRemainingPercent),
totalCostUsd,
estimatedFullQuotaUsd,
rows: breakdownRows,
};
}

View File

@@ -231,6 +231,27 @@ test("getApiKeyUsageLimitStatus cuts weekly USD spend at observed provider quota
null,
"2026-06-20T02:10:00.000Z"
);
db.prepare(
`
INSERT INTO provider_quota_reset_events
(provider, connection_id, window_key, window_started_at, window_resets_at,
observed_at, previous_remaining_percentage, new_remaining_percentage,
previous_used_percentage, new_used_percentage, raw_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
).run(
"claude",
"conn-claude",
"weekly (7d)",
"2026-06-18T23:00:00.000Z",
"2026-06-25T23:00:00.000Z",
"2026-06-18T23:04:00.000Z",
0,
100,
100,
0,
null
);
const metadata = await apiKeysDb.getApiKeyMetadata(created.key);
assert.ok(metadata);
@@ -287,25 +308,51 @@ test("buildApiKeyUsageLimitText returns API-key quota spend percentage and reset
assert.equal(
text,
[
"Cota diaria",
"Daily quota",
"$10.00",
"Gasto diario",
"Daily spent",
"$2.00",
"Uso diario",
"Daily used",
"20%",
"Resets in 7h",
"Resets in 7h 0m",
"",
"Cota semanal",
"Weekly quota",
"$50.00",
"Gasto semanal",
"Weekly spent",
"$5.25",
"Uso semanal",
"Weekly used",
"11%",
"Resets in 6d",
"Resets in 6d 0h 0m",
].join("\n")
);
});
test("buildApiKeyUsageLimitPercentText returns remaining percentages only", () => {
const text = usageLimits.buildApiKeyUsageLimitPercentText(
{
enabled: true,
dailyLimitUsd: 10,
weeklyLimitUsd: 50,
dailySpentUsd: 2,
weeklySpentUsd: 5.25,
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
weeklyResetAtIso: "2026-06-25T20:00:00.000Z",
dailyExceeded: false,
weeklyExceeded: false,
},
Date.parse("2026-06-19T20:00:00.000Z")
);
assert.equal(
text,
["Daily", "80% left", "⏱ reset in 7h 0m", "", "Weekly", "90% left", "⏱ reset in 6d 0h 0m"].join(
"\n"
)
);
});
test("buildApiKeyUsageLimitRejection includes over-quota percentage and reset hint", async () => {
const response = usageLimits.buildApiKeyUsageLimitRejection(
new Request("http://localhost/v1/messages", {
@@ -331,7 +378,37 @@ test("buildApiKeyUsageLimitRejection includes over-quota percentage and reset hi
const body = (await response.json()) as { error: { message: string } };
assert.equal(
body.error.message,
"This API key reached its weekly USD usage quota ($1.09 of $1.00, 109%). Resets in 6d. Choose another allowed model after reset."
"This API key reached its weekly USD usage quota ($1.09 of $1.00, 109%). Resets in 6d 0h 0m. Choose another allowed model after reset."
);
});
test("buildApiKeyUsageLimitRejection can hide USD amounts for client-facing policy errors", async () => {
const response = usageLimits.buildApiKeyUsageLimitRejection(
new Request("http://localhost/v1/messages", {
headers: { "anthropic-version": "2023-06-01" },
}),
{
enabled: true,
dailyLimitUsd: 10,
weeklyLimitUsd: 1,
dailySpentUsd: 0.25,
weeklySpentUsd: 1.09,
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
weeklyResetAtIso: "2026-06-25T20:00:00.000Z",
dailyExceeded: false,
weeklyExceeded: true,
},
Date.parse("2026-06-19T20:00:00.000Z"),
{ showUsd: false }
);
assert.equal(response.status, 400);
const body = (await response.json()) as { error: { message: string } };
assert.equal(
body.error.message,
"This API key reached its weekly usage quota (109%). Resets in 6d 0h 0m. Choose another allowed model after reset."
);
});

View File

@@ -89,89 +89,143 @@ test("buildUsageCommandText formats cached Claude usage windows exactly", async
getAllProviderLimitsCache: () => ({}),
isValidApiKey: async () => true,
getApiKeyMetadata: async () => null,
getQuotaPolicy: async () => ({
defaultThresholdPercent: 0,
providerWindowDefaults: {},
}),
}
);
assert.equal(
text,
[
"Plan",
"Claude Max",
"Provider quota",
"Session",
"47% left",
"⏱ reset in 9m",
"",
"Usage",
"Session (5hr)",
"53%",
"Resets in 9m",
"",
"Weekly (7 day)",
"72%",
"Resets in 1d",
"",
"Weekly Sonnet",
"30%",
"Resets in 1d",
"Weekly",
"28% left",
"⏱ reset in 1d 0h 0m",
].join("\n")
);
});
test("buildUsageCommandText formats API key USD limits when fair usage is enabled", async () => {
test("buildUsageCommandText formats API key USD limits as personal percentages", async () => {
let usageStatusPreferredProvider: string | null | undefined;
const text = await buildUsageCommandText(
{
id: "key-limited",
name: "limited",
allowedConnections: ["conn-claude"],
usageLimitEnabled: true,
dailyUsageLimitUsd: 10,
weeklyUsageLimitUsd: 50,
},
{
now: () => NOW,
getApiKeyUsageLimitStatus: async () => ({
enabled: true,
dailyLimitUsd: 10,
weeklyLimitUsd: 50,
dailySpentUsd: 2,
weeklySpentUsd: 5.25,
dailyWindowStartIso: "2026-06-16T03:00:00.000Z",
dailyResetAtIso: "2026-06-17T03:00:00.000Z",
weeklyWindowStartIso: "2026-06-09T12:00:00.000Z",
weeklyResetAtIso: "2026-06-23T12:00:00.000Z",
dailyExceeded: false,
weeklyExceeded: false,
}),
getProviderConnectionById: async () => {
throw new Error("provider connection lookup must not run for fair usage output");
},
getProviderConnections: async () => {
throw new Error("provider connection lookup must not run for fair usage output");
getApiKeyUsageLimitStatus: async (metadata) => {
usageStatusPreferredProvider = metadata.preferredProvider;
return {
enabled: true,
dailyLimitUsd: 10,
weeklyLimitUsd: 50,
dailySpentUsd: 2,
weeklySpentUsd: 5.25,
dailyWindowStartIso: "2026-06-16T03:00:00.000Z",
dailyResetAtIso: "2026-06-17T03:00:00.000Z",
weeklyWindowStartIso: "2026-06-09T12:00:00.000Z",
weeklyResetAtIso: "2026-06-23T12:00:00.000Z",
dailyExceeded: false,
weeklyExceeded: false,
};
},
getProviderConnectionById: async () => null,
getProviderConnections: async () => [],
getProviderLimitsCache: () => null,
getAllProviderLimitsCache: () => {
throw new Error("provider cache lookup must not run for fair usage output");
},
getAllProviderLimitsCache: () => ({}),
isValidApiKey: async () => true,
getApiKeyMetadata: async () => null,
getQuotaPolicy: async () => ({
defaultThresholdPercent: 0,
providerWindowDefaults: {},
}),
},
{ preferredProvider: "claude" }
);
assert.equal(usageStatusPreferredProvider, "claude");
assert.equal(
text,
[
"Personal quota",
"Daily",
"80% left",
"⏱ reset in 15h 0m",
"",
"Weekly",
"90% left",
"⏱ reset in 7d 0h 0m",
"",
"Provider quota",
"No cached usage data available.",
].join("\n")
);
});
test("buildUsageCommandText scales provider quota remaining by configured cutoffs", async () => {
const text = await buildUsageCommandText(
{
id: "key-cutoff",
name: "cutoff",
allowedConnections: ["conn-claude"],
},
{
now: () => NOW,
getProviderConnectionById: async () => ({
id: "conn-claude",
provider: "claude",
isActive: true,
quotaWindowThresholds: { "weekly (7d)": 10 },
}),
getProviderConnections: async () => [],
getProviderLimitsCache: () => ({
plan: "Claude Max",
quotas: {
"session (5h)": {
used: 0,
total: 100,
resetAt: new Date(NOW + 4 * 60 * 60_000 + 4 * 60_000).toISOString(),
},
"weekly (7d)": {
used: 90,
total: 100,
resetAt: new Date(NOW + 24 * 60 * 60_000 + 44 * 60_000).toISOString(),
},
},
message: null,
fetchedAt: new Date(NOW).toISOString(),
}),
getAllProviderLimitsCache: () => ({}),
isValidApiKey: async () => true,
getApiKeyMetadata: async () => null,
getQuotaPolicy: async () => ({
defaultThresholdPercent: 0,
providerWindowDefaults: {},
}),
}
);
assert.equal(
text,
[
"Cota diaria",
"$10.00",
"Gasto diario",
"$2.00",
"Uso diario",
"20%",
"Resets in 15h",
"Provider quota",
"Session",
"100% left",
"⏱ reset in 4h 4m",
"",
"Cota semanal",
"$50.00",
"Gasto semanal",
"$5.25",
"Uso semanal",
"11%",
"Resets in 7d",
"Weekly",
"0% left",
"⏱ reset in 1d 0h 44m",
].join("\n")
);
});
@@ -231,6 +285,10 @@ test("handleInternalUsageCommandHttpRequest returns terminal text for an allowed
fetchedAt: new Date(NOW).toISOString(),
},
getAllProviderLimitsCache: () => ({}),
getQuotaPolicy: async () => ({
defaultThresholdPercent: 0,
providerWindowDefaults: {},
}),
getApiKeyUsageLimitStatus: async () => {
throw new Error("usage limit lookup must not run for provider quota output");
},
@@ -242,21 +300,14 @@ test("handleInternalUsageCommandHttpRequest returns terminal text for an allowed
assert.equal(
await response.text(),
[
"Plan",
"Claude Max",
"Provider quota",
"Session",
"26% left",
"⏱ reset in 2h 0m",
"",
"Usage",
"Session (5hr)",
"74%",
"Resets in 2h",
"",
"Weekly (7 day)",
"25%",
"Resets in 6d",
"",
"Weekly Sonnet",
"Unavailable",
"Resets in unknown",
"Weekly",
"75% left",
"⏱ reset in 6d 0h 0m",
].join("\n")
);
});
@@ -433,6 +484,10 @@ test("handleInternalUsageCommand returns enabled usage snapshot locally", async
fetchedAt: new Date(NOW).toISOString(),
}),
getAllProviderLimitsCache: () => ({}),
getQuotaPolicy: async () => ({
defaultThresholdPercent: 0,
providerWindowDefaults: {},
}),
}
);
@@ -441,7 +496,7 @@ test("handleInternalUsageCommand returns enabled usage snapshot locally", async
const body = (await response.json()) as {
content: Array<{ type: string; text: string }>;
};
assert.equal(body.content[0].text.includes("Weekly Sonnet\n30%\nResets in 1d"), true);
assert.equal(body.content[0].text.includes("Weekly\n28% left\n⏱ reset in 1d 0h 0m"), true);
});
test("handleInternalUsageCommand ignores normal prompts", async () => {

View File

@@ -8,9 +8,11 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-quota-res
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../../src/lib/db/core.ts");
const { recordProviderQuotaResetEventIfChanged, getProviderQuotaWindowStartIso } = await import(
"../../../src/lib/db/quotaResetEvents.ts"
);
const {
recordProviderQuotaResetEventIfChanged,
getProviderQuotaWindowStart,
getProviderQuotaWindowStartIso,
} = await import("../../../src/lib/db/quotaResetEvents.ts");
// Force migrations (incl. 108_provider_quota_reset_events) to run.
core.getDbInstance();
@@ -49,13 +51,132 @@ test("getWindowStart returns null for a reset day with no recorded event", () =>
);
});
test("does not record when previous and current reset fall on the same day (no transition)", () => {
test("observed same-resetAt quota drop overrides an older recorded weekly window", () => {
const connectionId = "conn-early-reset-snapshot";
const targetResetAt = "2026-07-02T23:00:00.000Z";
const db = core.getDbInstance();
db.prepare(
`
INSERT INTO provider_quota_reset_events
(provider, connection_id, window_key, window_started_at, window_resets_at,
observed_at, previous_remaining_percentage, new_remaining_percentage,
previous_used_percentage, new_used_percentage, raw_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
).run(
"claude",
connectionId,
"weekly (7d)",
"2026-06-25T23:00:00.000Z",
targetResetAt,
"2026-06-25T23:04:00.000Z",
0,
100,
100,
0,
null
);
const insertSnapshot = db.prepare(`
INSERT INTO quota_snapshots (
provider,
connection_id,
window_key,
remaining_percentage,
is_exhausted,
next_reset_at,
window_duration_ms,
raw_data,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
insertSnapshot.run(
"claude",
connectionId,
"weekly (7d)",
100,
0,
targetResetAt,
null,
null,
"2026-06-25T23:04:00.000Z"
);
insertSnapshot.run(
"claude",
connectionId,
"weekly (7d)",
4,
0,
targetResetAt,
null,
null,
"2026-07-01T00:05:00.000Z"
);
insertSnapshot.run(
"claude",
connectionId,
"weekly (7d)",
100,
0,
targetResetAt,
null,
null,
"2026-07-01T21:41:13.293Z"
);
const start = getProviderQuotaWindowStart(
connectionId,
targetResetAt,
Date.parse("2026-07-02T00:00:00.000Z")
);
assert.deepEqual(start, {
windowStartIso: "2026-07-01T21:41:13.293Z",
source: "observed_snapshot_reset",
});
assert.equal(
getProviderQuotaWindowStartIso(
connectionId,
targetResetAt,
Date.parse("2026-07-02T00:00:00.000Z")
),
"2026-07-01T21:41:13.293Z"
);
});
test("records same-resetAt weekly resets when usage drops back to the reset floor", () => {
const connectionId = "conn-early-reset-record";
const targetResetAt = "2026-07-02T23:00:00.000Z";
const observedAt = "2026-07-01T21:41:13.293Z";
recordProviderQuotaResetEventIfChanged({
provider: "claude",
connectionId,
windowKey: "weekly (7d)",
currentResetAt: targetResetAt,
currentRemainingPercentage: 100,
previousObservation: { resetAt: targetResetAt, remainingPercentage: 4 },
observedAt,
});
assert.equal(
getProviderQuotaWindowStartIso(
connectionId,
targetResetAt,
Date.parse("2026-07-02T00:00:00.000Z")
),
observedAt
);
});
test("does not record when previous and current reset fall on the same day without a reset drop", () => {
recordProviderQuotaResetEventIfChanged({
provider: PROVIDER,
connectionId: "conn-sameday",
windowKey: "weekly",
currentResetAt: "2026-03-10T23:00:00.000Z",
currentRemainingPercentage: 80,
currentRemainingPercentage: 69,
previousObservation: { resetAt: "2026-03-10T01:00:00.000Z", remainingPercentage: 70 },
observedAt: "2026-03-10T23:30:00.000Z",
});

View File

@@ -0,0 +1,452 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-provider-costs-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
process.env.API_KEY_SECRET = "provider-window-costs-test-secret";
const core = await import("../../src/lib/db/core.ts");
const apiKeys = await import("../../src/lib/db/apiKeys.ts");
const localDb = await import("../../src/lib/localDb.ts");
const providerLimits = await import("../../src/lib/db/providerLimits.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
const costRules = await import("../../src/domain/costRules.ts");
const { getProviderWindowCostBreakdown } =
await import("../../src/lib/usage/providerWindowCosts.ts");
async function resetStorage() {
core.resetDbInstance();
apiKeys.resetApiKeyState();
costRules.resetCostData();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.beforeEach(async () => {
await resetStorage();
});
test.after(() => {
core.resetDbInstance();
apiKeys.resetApiKeyState();
costRules.resetCostData();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("Codex provider window costs use the weekly reset window and API key USD limit", async () => {
await localDb.updatePricing({
codex: {
"gpt-5.5": { input: 10, output: 20, cached: 1, cache_creation: 5, reasoning: 30 },
},
});
const key = await apiKeys.createApiKey("Codex Key", "machine-codex-window");
costRules.setBudget(key.id, {
dailyLimitUsd: 0,
weeklyLimitUsd: 40,
resetInterval: "weekly",
resetTime: "00:00",
});
providerLimits.setProviderLimitsCache("codex-conn", {
quotas: {
"session (5h)": {
used: 0,
total: 100,
remainingPercentage: 100,
resetAt: "2026-06-28T16:00:00.000Z",
},
"weekly (7d)": {
used: 13,
total: 100,
remainingPercentage: 87,
resetAt: "2026-07-02T23:00:00.000Z",
},
},
plan: "Prolite",
message: null,
fetchedAt: "2026-06-28T12:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "codex",
model: "gpt-5.5",
connectionId: "codex-conn",
apiKeyId: key.id,
apiKeyName: "Old Codex Key",
tokens: { input: 1_000_000, output: 0 },
timestamp: "2026-06-26T00:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "codex",
model: "gpt-5.5",
connectionId: "codex-conn",
apiKeyId: key.id,
apiKeyName: "Old Codex Key",
tokens: { input: 1_000_000, output: 0 },
timestamp: "2026-06-25T22:59:59.000Z",
});
const result = await getProviderWindowCostBreakdown({
provider: "codex",
connectionId: "codex-conn",
now: Date.parse("2026-06-28T12:00:00.000Z"),
});
assert.equal(result.windowStartAt, "2026-06-25T23:00:00.000Z");
assert.equal(result.windowResetAt, "2026-07-02T23:00:00.000Z");
assert.equal(result.windowSource, "provider_weekly_reset");
assert.equal(result.quotaUsedPercent, 13);
assert.equal(result.totalCostUsd, 10);
assert.equal(result.estimatedFullQuotaUsd, 76.923077);
assert.equal(result.rows.length, 1);
assert.equal(result.rows[0].apiKeyName, "Codex Key");
assert.equal(result.rows[0].costUsd, 10);
assert.equal(result.rows[0].limitUsd, 40);
assert.equal(result.rows[0].limitUsedPercent, 25);
});
test("Claude provider window costs split spending across API keys from the current weekly window", async () => {
await localDb.updatePricing({
claude: {
"claude-sonnet-4": { input: 3, output: 15, cached: 0.3, cache_creation: 3.75 },
},
});
const heavyKey = await apiKeys.createApiKey("Claude Heavy", "machine-claude-heavy");
const lightKey = await apiKeys.createApiKey("Claude Light", "machine-claude-light");
costRules.setBudget(heavyKey.id, {
dailyLimitUsd: 0,
weeklyLimitUsd: 20,
resetInterval: "weekly",
resetTime: "00:00",
});
providerLimits.setProviderLimitsCache("claude-conn", {
quotas: {
"Session (5hr)": {
used: 2,
total: 100,
remainingPercentage: 98,
resetAt: "2026-06-28T15:30:00.000Z",
},
"Weekly (7 day)": {
used: 54,
total: 100,
remainingPercentage: 46,
resetAt: "2026-07-02T23:00:00.000Z",
},
"Weekly Sonnet": {
used: 18,
total: 100,
remainingPercentage: 82,
resetAt: "2026-07-02T23:00:00.000Z",
},
},
plan: "default_claude_max_20x",
message: null,
fetchedAt: "2026-06-28T12:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-sonnet-4",
connectionId: "claude-conn",
apiKeyId: heavyKey.id,
apiKeyName: "Heavy old",
tokens: { input: 1_000_000, output: 0 },
timestamp: "2026-06-26T00:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-sonnet-4",
connectionId: "claude-conn",
apiKeyId: lightKey.id,
apiKeyName: "Light old",
tokens: { input: 500_000, output: 0 },
timestamp: "2026-06-27T00:00:00.000Z",
});
const result = await getProviderWindowCostBreakdown({
provider: "claude",
connectionId: "claude-conn",
now: Date.parse("2026-06-28T12:00:00.000Z"),
});
assert.equal(result.windowStartAt, "2026-06-25T23:00:00.000Z");
assert.equal(result.quotaName, "Weekly (7 day)");
assert.equal(result.quotaUsedPercent, 54);
assert.equal(result.quotaRemainingPercent, 46);
assert.equal(result.totalCostUsd, 4.5);
assert.equal(result.estimatedFullQuotaUsd, 8.333333);
assert.equal(result.rows.length, 2);
assert.equal(result.rows[0].apiKeyName, "Claude Heavy");
assert.equal(result.rows[0].costUsd, 3);
assert.equal(result.rows[0].limitUsd, 20);
assert.equal(result.rows[0].limitUsedPercent, 15);
assert.equal(result.rows[1].apiKeyName, "Claude Light");
assert.equal(result.rows[1].costUsd, 1.5);
assert.equal(result.rows[1].limitUsd, null);
});
test("provider window costs use the recorded reset event as the cost cutoff", async () => {
await localDb.updatePricing({
claude: {
"claude-opus-4-8": { input: 1, output: 1, cached: 1, cache_creation: 1, reasoning: 1 },
},
});
providerLimits.setProviderLimitsCache("claude-reset-event", {
quotas: {
"weekly (7d)": {
used: 25,
total: 100,
remainingPercentage: 75,
resetAt: "2026-07-01T10:00:00.000Z",
},
},
plan: "default_claude_max_20x",
message: null,
fetchedAt: "2026-06-25T12:00:00.000Z",
});
core
.getDbInstance()
.prepare(
`
INSERT INTO provider_quota_reset_events
(provider, connection_id, window_key, window_started_at, window_resets_at,
observed_at, previous_remaining_percentage, new_remaining_percentage,
previous_used_percentage, new_used_percentage, raw_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
)
.run(
"claude",
"claude-reset-event",
"weekly (7d)",
"2026-06-24T12:00:00.000Z",
"2026-07-01T10:00:00.000Z",
"2026-06-24T12:01:00.000Z",
0,
100,
100,
0,
null
);
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-opus-4-8",
connectionId: "claude-reset-event",
tokens: { input: 5_000_000, output: 0 },
timestamp: "2026-06-24T11:30:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-opus-4-8",
connectionId: "claude-reset-event",
tokens: { input: 500_000, output: 0 },
timestamp: "2026-06-24T12:30:00.000Z",
});
const result = await getProviderWindowCostBreakdown({
provider: "claude",
connectionId: "claude-reset-event",
now: Date.parse("2026-06-25T12:00:00.000Z"),
});
assert.equal(result.windowStartAt, "2026-06-24T12:00:00.000Z");
assert.equal(result.totalCostUsd, 0.5);
assert.equal(result.estimatedFullQuotaUsd, 2);
assert.equal(result.rows.length, 1);
assert.equal(result.rows[0].requests, 1);
});
test("provider window costs cut at an observed same-resetAt quota reset", async () => {
await localDb.updatePricing({
claude: {
"claude-opus-4-8": { input: 1, output: 1, cached: 1, cache_creation: 1, reasoning: 1 },
},
});
const targetResetAt = "2026-07-02T23:00:00.000Z";
providerLimits.setProviderLimitsCache("claude-early-reset", {
quotas: {
"weekly (7d)": {
used: 7,
total: 100,
remainingPercentage: 93,
resetAt: targetResetAt,
},
},
plan: "default_claude_max_20x",
message: null,
fetchedAt: "2026-07-02T00:04:00.000Z",
});
const db = core.getDbInstance();
db.prepare(
`
INSERT INTO provider_quota_reset_events
(provider, connection_id, window_key, window_started_at, window_resets_at,
observed_at, previous_remaining_percentage, new_remaining_percentage,
previous_used_percentage, new_used_percentage, raw_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
).run(
"claude",
"claude-early-reset",
"weekly (7d)",
"2026-06-25T23:00:00.000Z",
targetResetAt,
"2026-06-25T23:04:00.000Z",
0,
100,
100,
0,
null
);
const insertSnapshot = db.prepare(`
INSERT INTO quota_snapshots (
provider,
connection_id,
window_key,
remaining_percentage,
is_exhausted,
next_reset_at,
window_duration_ms,
raw_data,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
insertSnapshot.run(
"claude",
"claude-early-reset",
"weekly (7d)",
4,
0,
targetResetAt,
null,
null,
"2026-07-01T00:05:00.000Z"
);
insertSnapshot.run(
"claude",
"claude-early-reset",
"weekly (7d)",
100,
0,
targetResetAt,
null,
null,
"2026-07-01T21:41:13.293Z"
);
insertSnapshot.run(
"claude",
"claude-early-reset",
"weekly (7d)",
93,
0,
targetResetAt,
null,
null,
"2026-07-02T00:04:00.000Z"
);
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-opus-4-8",
connectionId: "claude-early-reset",
tokens: { input: 8_000_000, output: 0 },
timestamp: "2026-07-01T20:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-opus-4-8",
connectionId: "claude-early-reset",
tokens: { input: 2_000_000, output: 0 },
timestamp: "2026-07-01T22:00:00.000Z",
});
const result = await getProviderWindowCostBreakdown({
provider: "claude",
connectionId: "claude-early-reset",
now: Date.parse("2026-07-02T00:10:00.000Z"),
});
assert.equal(result.windowStartAt, "2026-07-01T21:41:13.293Z");
assert.equal(result.windowStartSource, "observed_snapshot_reset");
assert.equal(result.totalCostUsd, 2);
assert.equal(result.rows.length, 1);
assert.equal(result.rows[0].requests, 1);
});
test("provider window costs prefer recorded USD history over repricing usage tokens", async () => {
await localDb.updatePricing({
claude: {
"claude-opus-4-8": { input: 1, output: 1, cached: 0.1, cache_creation: 1, reasoning: 1 },
},
});
const key = await apiKeys.createApiKey("Recorded USD Key", "machine-recorded-usd");
providerLimits.setProviderLimitsCache("claude-recorded-cost", {
quotas: {
"weekly (7d)": {
used: 50,
total: 100,
remainingPercentage: 50,
resetAt: "2026-07-01T10:00:00.000Z",
},
},
plan: "default_claude_max_20x",
message: null,
fetchedAt: "2026-06-25T12:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-opus-4-8",
connectionId: "claude-recorded-cost",
apiKeyId: key.id,
apiKeyName: "Recorded old",
tokens: { input: 1_000_000, cacheRead: 1_000_000, output: 0 },
timestamp: "2026-06-24T10:00:00.000Z",
});
await usageHistory.saveRequestUsage({
provider: "claude",
model: "claude-opus-4-8",
connectionId: "claude-recorded-cost",
apiKeyId: key.id,
apiKeyName: "Recorded old",
tokens: { input: 1_000_000, cacheRead: 1_000_000, output: 0 },
timestamp: "2026-06-24T10:01:00.000Z",
});
core
.getDbInstance()
.prepare("INSERT INTO domain_cost_history (api_key_id, cost, timestamp) VALUES (?, ?, ?)")
.run(key.id, 10, Date.parse("2026-06-24T10:00:00.010Z"));
core
.getDbInstance()
.prepare("INSERT INTO domain_cost_history (api_key_id, cost, timestamp) VALUES (?, ?, ?)")
.run(key.id, 7, Date.parse("2026-06-24T10:01:00.010Z"));
const result = await getProviderWindowCostBreakdown({
provider: "claude",
connectionId: "claude-recorded-cost",
now: Date.parse("2026-06-25T12:00:00.000Z"),
});
assert.equal(result.totalCostUsd, 17);
assert.equal(result.estimatedFullQuotaUsd, 34);
assert.equal(result.rows.length, 1);
assert.equal(result.rows[0].apiKeyName, "Recorded USD Key");
assert.equal(result.rows[0].costUsd, 17);
});