Merge pull request #2907 from jakoss/github-usage-rework

Align GitHub Copilot quota usage with AI credits
This commit is contained in:
Bohdan Triapitsyn
2026-08-28 01:23:40 +03:00
committed by GitHub
17 changed files with 232 additions and 32 deletions
@@ -19,6 +19,7 @@ const AUTH = JSON.stringify({
'opencode-go': { key: 'test-token' },
'zai-coding-plan': { key: 'test-token' },
deepseek: { key: 'test-token' },
'github-copilot': { access: 'test-token' },
anthropic: { access: 'test-token', refresh: 'test-refresh' },
});
((fs as unknown) as { existsSync: () => boolean }).existsSync = () => true;
@@ -196,6 +197,71 @@ describe('Codex quota provider (VS Code parity)', () => {
});
});
describe('GitHub Copilot quota provider (VS Code parity)', () => {
test('exposes only premium interactions as the primary usage window', async () => {
stubFetchReturning(() => Promise.resolve(mockResponse({
quota_reset_date: '2026-09-01T00:00:00Z',
quota_snapshots: {
chat: { entitlement: 100, remaining: 80 },
completions: { entitlement: 1000, remaining: 900 },
premium_interactions: { entitlement: 300, remaining: 225 },
},
})));
const result = await fetchQuotaForProvider('github-copilot');
assert.equal(result.ok, true);
assert.deepEqual(Object.keys(result.usage!.windows), ['premium_interactions']);
assert.equal(result.usage!.windows.premium_interactions!.usedPercent, 25);
assert.equal(result.usage!.windows.premium_interactions!.valueLabel, '225 / 300 left');
});
test('add-on path mirrors the primary window shaping', async () => {
stubFetchReturning(() => Promise.resolve(mockResponse({
quota_reset_date: '2026-09-01T00:00:00Z',
quota_snapshots: {
premium_interactions: { entitlement: 300, remaining: 225 },
},
})));
const result = await fetchQuotaForProvider('github-copilot-addon');
assert.equal(result.ok, true);
assert.deepEqual(Object.keys(result.usage!.windows), ['premium_interactions']);
assert.equal(result.usage!.windows.premium_interactions!.usedPercent, 25);
});
test('reports unlimited plans without a percent', async () => {
stubFetchReturning(() => Promise.resolve(mockResponse({
quota_reset_date: '2026-09-01T00:00:00Z',
quota_snapshots: {
premium_interactions: { unlimited: true, entitlement: -1, remaining: -1 },
},
})));
const result = await fetchQuotaForProvider('github-copilot');
assert.equal(result.ok, true);
assert.equal(result.usage!.windows.premium_interactions!.usedPercent, null);
assert.equal(result.usage!.windows.premium_interactions!.valueLabel, 'Unlimited');
});
test('falls back to percent_remaining when entitlement is unusable', async () => {
stubFetchReturning(() => Promise.resolve(mockResponse({
quota_reset_date: '2026-09-01T00:00:00Z',
quota_snapshots: {
premium_interactions: { entitlement: 0, remaining: 0, percent_remaining: 75.5 },
},
})));
const result = await fetchQuotaForProvider('github-copilot');
assert.equal(result.ok, true);
assert.ok(Math.abs(result.usage!.windows.premium_interactions!.usedPercent! - 24.5) < 1e-9);
assert.equal(result.usage!.windows.premium_interactions!.valueLabel, undefined);
});
});
describe('Claude quota provider (VS Code parity)', () => {
test('parses current limits, model-scoped limits, and extra usage', async () => {
stubFetchReturning(() => Promise.resolve(mockResponse({
+26 -10
View File
@@ -1465,14 +1465,35 @@ const buildCopilotWindows = (payload: Record<string, unknown>) => {
const resetAt = toTimestamp(payload.quota_reset_date);
const windows: Record<string, UsageWindow> = {};
// Mirrors the quota semantics of microsoft/vscode-copilot-chat
// (CopilotUserQuotaInfo): each snapshot carries entitlement, remaining,
// unlimited, and percent_remaining. Unlimited plans report no usable
// entitlement; percent_remaining is a server-computed fallback.
const addWindow = (label: string, snapshot?: Record<string, unknown>) => {
if (!snapshot) return;
if (snapshot.unlimited === true) {
windows[label] = toUsageWindow({
usedPercent: null,
windowSeconds: null,
resetAt,
valueLabel: 'Unlimited',
});
return;
}
const entitlement = toNumber(snapshot.entitlement);
const remaining = toNumber(snapshot.remaining);
const usedPercent = entitlement && remaining !== null
? Math.max(0, Math.min(100, 100 - (remaining / entitlement) * 100))
let usedPercent = entitlement !== null && entitlement > 0 && remaining !== null
? Math.min(100, Math.max(0, 100 - (remaining / entitlement) * 100))
: null;
const valueLabel = entitlement !== null && remaining !== null
if (usedPercent === null) {
const percentRemaining = toNumber(snapshot.percent_remaining);
if (percentRemaining !== null) {
usedPercent = Math.min(100, Math.max(0, 100 - percentRemaining));
}
}
const valueLabel = entitlement !== null && entitlement > 0 && remaining !== null
? `${remaining.toFixed(0)} / ${entitlement.toFixed(0)} left`
: null;
windows[label] = toUsageWindow({
@@ -1483,9 +1504,7 @@ const buildCopilotWindows = (payload: Record<string, unknown>) => {
});
};
addWindow('chat', quota.chat as Record<string, unknown> | undefined);
addWindow('completions', quota.completions as Record<string, unknown> | undefined);
addWindow('premium', quota.premium_interactions as Record<string, unknown> | undefined);
addWindow('premium_interactions', quota.premium_interactions as Record<string, unknown> | undefined);
return windows;
};
@@ -1582,15 +1601,12 @@ const fetchCopilotAddonQuota = async (): Promise<ProviderResult> => {
}
const payload = await response.json() as Record<string, unknown>;
const windows = buildCopilotWindows(payload);
const premium = windows.premium ? { premium: windows.premium } : windows;
return buildResult({
providerId: 'github-copilot-addon',
providerName: 'GitHub Copilot Add-on',
ok: true,
configured: true,
usage: { windows: premium },
usage: { windows: buildCopilotWindows(payload) },
});
} catch (error) {
return buildResult({