Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions docs/providers/copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@ X-Github-Api-Version: 2025-04-01

## Displayed Lines

| Line | Tier | Description |
|--------------|------|------------------------------------------|
| Premium | Paid | Premium interactions remaining (percent) |
| Chat | Both | Chat messages remaining |
| Completions | Free | Code completions remaining |
| Line | Tier | Description |
|----------------|------|------------------------------------------------------|
| Premium | Paid | Premium interactions remaining (percent) |
| Requests Used | Paid | Premium requests used vs. entitlement (e.g. 60 / 300)|
| Chat | Both | Chat messages remaining |
| Completions | Free | Code completions remaining |

All progress lines include:
- `resetsAt` — ISO timestamp of next quota reset
Expand Down
10 changes: 10 additions & 0 deletions plugins/copilot/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@
);
if (premiumLine) lines.push(premiumLine);

// Show raw request counts for premium interactions
const pi = snapshots.premium_interactions;
if (pi && typeof pi.entitlement === "number" && typeof pi.remaining === "number" && pi.entitlement > 0) {
var used = pi.entitlement - pi.remaining;
lines.push(ctx.line.text({
label: "Requests Used",
value: String(Math.max(0, used)) + " / " + String(pi.entitlement),
}));
Comment on lines +231 to +235
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid var here; the rest of this plugin uses const/let. Using const used = ... (or let if you intend to reassign) prevents hoisting-related surprises and keeps style consistent.

Copilot uses AI. Check for mistakes.
}

const chatLine = makeProgressLine(
ctx,
"Chat",
Expand Down
1 change: 1 addition & 0 deletions plugins/copilot/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"brandColor": "#A855F7",
"lines": [
{ "type": "progress", "label": "Premium", "scope": "overview", "primaryOrder": 1 },
{ "type": "text", "label": "Requests Used", "scope": "detail" },
{ "type": "progress", "label": "Chat", "scope": "overview", "primaryOrder": 2 },
{ "type": "progress", "label": "Completions", "scope": "overview" }
]
Expand Down
76 changes: 76 additions & 0 deletions plugins/copilot/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,82 @@ describe("copilot plugin", () => {
expect(chat.used).toBe(5); // 100 - 95
});

it("shows Requests Used text line for paid tier", async () => {
const ctx = makePluginTestContext();
setKeychainToken(ctx, "tok");
mockUsageOk(ctx);
const plugin = await loadPlugin();
const result = plugin.probe(ctx);
const reqLine = result.lines.find((l) => l.label === "Requests Used");
expect(reqLine).toBeTruthy();
expect(reqLine.type).toBe("text");
expect(reqLine.value).toBe("60 / 300"); // 300 - 240 = 60
});

it("omits Requests Used when entitlement/remaining are missing", async () => {
const ctx = makePluginTestContext();
setKeychainToken(ctx, "tok");
ctx.host.http.request.mockReturnValue({
status: 200,
bodyText: JSON.stringify(
makeUsageResponse({
quota_snapshots: {
premium_interactions: {
percent_remaining: 80,
quota_id: "premium",
},
},
}),
),
});
const plugin = await loadPlugin();
const result = plugin.probe(ctx);
expect(result.lines.find((l) => l.label === "Requests Used")).toBeFalsy();
});

it("clamps Requests Used to 0 when remaining exceeds entitlement", async () => {
const ctx = makePluginTestContext();
setKeychainToken(ctx, "tok");
ctx.host.http.request.mockReturnValue({
status: 200,
bodyText: JSON.stringify(
makeUsageResponse({
quota_snapshots: {
premium_interactions: {
percent_remaining: 120,
entitlement: 300,
remaining: 360,
quota_id: "premium",
},
},
}),
),
});
const plugin = await loadPlugin();
const result = plugin.probe(ctx);
const reqLine = result.lines.find((l) => l.label === "Requests Used");
expect(reqLine).toBeTruthy();
expect(reqLine.value).toBe("0 / 300");
});

it("omits Requests Used for free tier", async () => {
const ctx = makePluginTestContext();
setKeychainToken(ctx, "tok");
ctx.host.http.request.mockReturnValue({
status: 200,
bodyText: JSON.stringify({
copilot_plan: "individual",
access_type_sku: "free_limited_copilot",
limited_user_quotas: { chat: 410, completions: 4000 },
monthly_quotas: { chat: 500, completions: 4000 },
limited_user_reset_date: "2026-02-11",
}),
});
const plugin = await loadPlugin();
const result = plugin.probe(ctx);
expect(result.lines.find((l) => l.label === "Requests Used")).toBeFalsy();
});

it("renders only Premium when Chat is missing", async () => {
const ctx = makePluginTestContext();
setKeychainToken(ctx, "tok");
Expand Down
Loading