fix(quota): match the Claude Code integration provider to Claude quota

The opencode-claude integration registers its provider as `claude-code`, which
never matched the `claude` quota provider, so the collapsed Usage section in
the work status panel showed no limit for a model from that integration.

Claude windows also reported no duration, leaving the headline to fall back to
whichever row came first instead of the limit that runs out soonest. The
session and weekly windows now carry their length; extra usage stays without
one because it is a monthly spend cap.
This commit is contained in:
Bohdan Triapitsyn
2026-08-14 21:07:55 +03:00
parent b77a30cd88
commit 72cd861eb8
4 changed files with 31 additions and 7 deletions
@@ -16,6 +16,11 @@ const SESSION_WINDOW = '5h';
const WEEKLY_WINDOW = '7d';
const EXTRA_USAGE_WINDOW = 'extra_usage';
// Consumers rank limits by how soon they run out, so each window carries its
// duration. Extra usage is a monthly spend cap, not a rolling window.
const SESSION_WINDOW_SECONDS = 5 * 60 * 60;
const WEEKLY_WINDOW_SECONDS = 7 * 24 * 60 * 60;
const asArray = (value) => (Array.isArray(value) ? value : []);
/** Money in Anthropic's minor-unit form, e.g. `{ amount_minor: 10000, exponent: 2 }`. */
@@ -35,9 +40,9 @@ const formatSpendLabel = (used, limit, currency) => {
return limitLabel === null ? `${prefix}${usedLabel}` : `${prefix}${usedLabel} / ${prefix}${limitLabel}`;
};
const addWindow = (target, key, { percent, resetAt, valueLabel }) => {
const addWindow = (target, key, { percent, resetAt, valueLabel, windowSeconds = null }) => {
if (percent === null && !valueLabel) return;
target[key] = toUsageWindow({ usedPercent: percent, windowSeconds: null, resetAt, valueLabel });
target[key] = toUsageWindow({ usedPercent: percent, windowSeconds, resetAt, valueLabel });
};
const applyLimitsArray = (limits, windows, models) => {
@@ -49,16 +54,16 @@ const applyLimitsArray = (limits, windows, models) => {
const modelName = asNonEmptyString(asObject(asObject(limit.scope)?.model)?.display_name);
if (limit.kind === 'session') {
addWindow(windows, SESSION_WINDOW, { percent, resetAt });
addWindow(windows, SESSION_WINDOW, { percent, resetAt, windowSeconds: SESSION_WINDOW_SECONDS });
continue;
}
if (limit.kind === 'weekly_all') {
addWindow(windows, WEEKLY_WINDOW, { percent, resetAt });
addWindow(windows, WEEKLY_WINDOW, { percent, resetAt, windowSeconds: WEEKLY_WINDOW_SECONDS });
continue;
}
if (limit.kind === 'weekly_scoped' && modelName) {
const modelWindows = {};
addWindow(modelWindows, WEEKLY_WINDOW, { percent, resetAt });
addWindow(modelWindows, WEEKLY_WINDOW, { percent, resetAt, windowSeconds: WEEKLY_WINDOW_SECONDS });
if (Object.keys(modelWindows).length > 0) models[modelName] = { windows: modelWindows };
}
}
@@ -70,13 +75,15 @@ const applyLegacyFields = (payload, windows) => {
if (fiveHour) {
addWindow(windows, SESSION_WINDOW, {
percent: toNumber(fiveHour.utilization),
resetAt: toTimestamp(fiveHour.resets_at)
resetAt: toTimestamp(fiveHour.resets_at),
windowSeconds: SESSION_WINDOW_SECONDS
});
}
if (sevenDay) {
addWindow(windows, WEEKLY_WINDOW, {
percent: toNumber(sevenDay.utilization),
resetAt: toTimestamp(sevenDay.resets_at)
resetAt: toTimestamp(sevenDay.resets_at),
windowSeconds: WEEKLY_WINDOW_SECONDS
});
}
};
@@ -39,6 +39,15 @@ describe('Claude usage transforms', () => {
expect(models.Fable.windows['7d'].usedPercent).toBe(12);
});
it('reports each window duration so callers can rank limits by urgency', () => {
const { windows, models } = toClaudeUsage(LIVE_PAYLOAD);
expect(windows['5h'].windowSeconds).toBe(5 * 60 * 60);
expect(windows['7d'].windowSeconds).toBe(7 * 24 * 60 * 60);
expect(models.Fable.windows['7d'].windowSeconds).toBe(7 * 24 * 60 * 60);
expect(windows.extra_usage.windowSeconds).toBeNull();
});
it('reports extra usage as a spend window with a money label', () => {
const { windows } = toClaudeUsage(LIVE_PAYLOAD);