Add Usage Insights canvas extension 🤖🤖🤖 - #2939
Conversation
Contribute the Usage Insights canvas with current and historical token and AI-credit metrics, per-agent rollups, in-canvas session drill-down, installable plugin metadata, and a synthetic marketplace preview. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
🔒 PR Risk Scan ResultsScanned 8 changed file(s).
✅ No matching risk patterns were detected in changed files. Skipped non-text or missing files
|
There was a problem hiding this comment.
🟡 Changes recommended
The unauthenticated local server, path traversal risk, and several dashboard accuracy/navigation bugs must be addressed.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a reusable Usage Insights canvas extension and installable plugin for local Copilot usage analytics.
Changes:
- Implements read-only usage aggregation and session drill-down.
- Adds a responsive canvas UI with live refresh.
- Registers the plugin and updates generated catalogs.
File summaries
| File | Description |
|---|---|
extensions/usage-insights/extension.mjs |
Defines the canvas, local server, actions, and events. |
extensions/usage-insights/stats.mjs |
Aggregates usage from local SQLite databases. |
extensions/usage-insights/renderer.mjs |
Renders the interactive metrics dashboard. |
extensions/usage-insights/copilot-extension.json |
Declares extension metadata. |
extensions/usage-insights/README.md |
Documents behavior, privacy, and requirements. |
extensions/usage-insights/assets/preview.png |
Provides the marketplace preview. |
plugins/usage-insights/plugin.json |
Registers the installable plugin. |
plugins/usage-insights/README.md |
Documents installation and features. |
docs/README.plugins.md |
Adds the plugin to documentation. |
.github/plugin/marketplace.json |
Adds the marketplace entry. |
Review details
Suppressed comments (1)
extensions/usage-insights/renderer.mjs:755
- Cache-write tokens are collected and explicitly advertised in the extension README, but this breakdown never renders them and therefore undercounts
total tokens. Add a cache-write entry to the displayed values.
const values = [
{ label: 'Input', value: totals.inputTokens, color: 1 },
{ label: 'Cache read', value: totals.cacheReadTokens, color: 2 },
{ label: 'Output', value: totals.outputTokens, color: 3 },
{ label: 'Reasoning', value: totals.reasoningTokens, color: 5 },
];
- Files reviewed: 9/10 changed files
- Comments generated: 6
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Protect every loopback route with a per-instance capability token, constrain session metadata reads to the session-state root, correct cache-token accounting, reset empty timeline state, cancel superseded loads, and make back navigation target the current session. Include the required regenerated agent documentation and refreshed synthetic preview. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The renderer reuses stale bootstrap data during interaction and has accessibility and redundant-query issues.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
extensions/usage-insights/renderer.mjs:919
- The server already computed and embedded
bootstrap, but startup ignores it and immediately repeats every dashboard query while the page remains on placeholders. Render the snapshot and clear the loading state; the existing interval can perform the next refresh.
load();
extensions/usage-insights/renderer.mjs:541
aria-labeldoes not turn this generic<div>into a named control group, so screen-reader users encounter two toggle buttons without the “Agent metric” context. Give the container an explicit group role.
<div class="segmented" id="agentMetricControl" aria-label="Agent metric">
extensions/usage-insights/renderer.mjs:563
- This range selector also needs an explicit group role; otherwise its
aria-labeldoes not provide context for the dynamically inserted toggle buttons.
<div class="segmented" id="rangeControl" aria-label="History range"></div>
- Files reviewed: 10/11 changed files
- Comments generated: 2
- Review effort level: Balanced
Render the embedded dashboard snapshot immediately, avoid restoring stale bootstrap data during range changes, preserve valid heading semantics, and expose toggle context as explicit accessibility groups. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Live totals can become stale after the first persisted call, and two responsive UI behaviors violate accessibility expectations.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
extensions/usage-insights/renderer.mjs:824
- This always forces animated scrolling even when the user has enabled reduced motion; the CSS media query does not override an explicit
scrollTo({ behavior: 'smooth' })option. Respect the media preference here or use non-animated scrolling.
window.scrollTo({ top: 0, behavior: 'smooth' });
- Files reviewed: 10/11 changed files
- Comments generated: 2
- Review effort level: Balanced
Prefer runtime metrics for the active session, preserve an accessible back-button name on narrow layouts, and avoid forced animated scrolling when opening session history. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Token totals are double-counted, optional runtime credit data can mask persisted totals, and sub-second durations are misreported.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
extensions/usage-insights/renderer.mjs:625
- Durations below one second are all rendered as
1s, so a 1 ms call is overstated by 1000× even though the source value is available in milliseconds. Preserve millisecond precision for sub-second calls.
extensions/usage-insights/renderer.mjs:730 - The SDK defines
reasoningTokensas a subset ofoutputTokens, so adding both inflates the agent's token metric whenever reasoning is present. Exclude reasoning from this sum while retaining it as a separate breakdown value.
This issue also appears on line 766 of the same file.
extensions/usage-insights/renderer.mjs:766
- This total double-counts reasoning tokens because the SDK specifies that
reasoningTokensis already included inoutputTokens. The displayed “total tokens” should sum only non-overlapping categories; the reasoning row can remain as an informational subset.
const total = values.reduce((sum, entry) => sum + entry.value, 0);
- Files reviewed: 10/11 changed files
- Comments generated: 1
- Review effort level: Balanced
Merge persisted and runtime aggregates without losing credits, avoid reasoning-token double counting in totals, and preserve millisecond precision for sub-second model calls. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The renderer can misstate low credit totals and produce malformed agent metadata when model names are absent.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
extensions/usage-insights/renderer.mjs:740
- Rows with no recorded model produce metadata such as
· 3 callsbecause the empty model string is concatenated unconditionally. Filter empty parts as the session metadata renderer already does so these rows display cleanly.
left.append(element('span', 'agent-detail', entry.agent.models.join(', ') + ' · ' + entry.agent.calls + ' calls'));
- Files reviewed: 10/11 changed files
- Comments generated: 1
- Review effort level: Balanced
Keep real credit totals for chart labels while using separate nonzero plotting denominators, and scale sub-unit agent/token bars without distorting their displayed values. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Token totals currently double-count cache reads and writes, inflating displayed usage.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
extensions/usage-insights/renderer.mjs:731
- The per-agent “Tokens” value double-counts cached input. Copilot reports
inputTokensas cache-inclusive, so addingcacheReadTokensandcacheWriteTokensinflates every agent bar (while reasoning is correctly left out because it is included in output). Use input plus output for the aggregate; keep the cache fields only as detail metrics.
This issue also appears on line 767 of the same file.
extensions/usage-insights/renderer.mjs:770
- This “total tokens” calculation double-counts cache reads and writes because
inputTokensalready includes both cache categories. As a result, the displayed total exceeds the actual input-plus-output usage whenever caching occurs; calculate the total from input and output only.
const total = totals.inputTokens
+ totals.cacheReadTokens
+ totals.cacheWriteTokens
+ totals.outputTokens;
- Files reviewed: 10/11 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Treat input and output as the aggregate token categories while retaining cache and reasoning as detail rows, and format agent metadata cleanly when no model name is recorded. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Live aggregate totals can disagree with database-backed breakdowns, and an optional SDK metric currently causes stale AI-credit values.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 10/11 changed files
- Comments generated: 3
- Review effort level: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c54874d4-f708-414b-baf5-03241f052fdd
There was a problem hiding this comment.
🔵 Needs a closer look
An incompatible optional data.db schema can currently make every dashboard request fail.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
extensions/usage-insights/stats.mjs:181
- Treat
data.dbas optional by schema, not just by existence. Valid Copilot installations can have asessionstable withouttitle,agent, ormode; assigning that database here makes both metadata queries throw and turns every dashboard refresh into a 500 even thoughsession-store.dbcontains the required usage data. Probe the columns (or the full metadata query) and fall back to session-store metadata when the optional database is incompatible.
- Files reviewed: 10/11 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c54874d4-f708-414b-baf5-03241f052fdd
There was a problem hiding this comment.
🟡 Changes recommended
The dashboard can fail against the current database schema, and range and timeline timestamps are handled incorrectly.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
extensions/usage-insights/stats.mjs:308
- SQLite's default
created_atvalue is UTC text inYYYY-MM-DD HH:MM:SSform, whichDate.parseinterprets as local time in Node rather than UTC. On non-UTC systems this shifts the chart's start/end labels by the local offset. Normalize the database timestamp to an explicit UTC ISO value before passing it to JavaScript.
extensions/usage-insights/stats.mjs:352 assistant_usage_events.created_atuses SQLite'sdatetime('now')text format (YYYY-MM-DD HH:MM:SS), but the cutoff parameter is ISO text withT/Z. A raw text comparison excludes later events on the cutoff calendar day because a space sorts beforeT, so the 24h/7d/30d totals, agent split, and model rollups undercount that boundary day. Normalize both operands as SQLite datetimes.
extensions/usage-insights/stats.mjs:413- This repeats the raw mixed-format timestamp comparison for the highest-cost sessions query, so fixing the shared
whereSqlabove would still leave the history list missing events from the cutoff day. Normalize both values here as well.
- Files reviewed: 10/11 changed files
- Comments generated: 1
- Review effort level: Balanced
Handle partial optional app metadata schemas and normalize SQLite timestamps for UTC parsing and range comparisons. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c54874d4-f708-414b-baf5-03241f052fdd
There was a problem hiding this comment.
🔵 Needs a closer look
The substantial new loopback HTTP, UI, and local SQLite data-access surface warrants final human runtime and privacy validation.
Review details
- Files reviewed: 10/11 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Description
Adds the Usage Insights canvas as a reusable canvas extension plus a matching installable plugin.
The canvas provides current and historical GitHub Copilot token and AI-credit metrics, per-agent and sub-agent rollups, model-call details, and in-canvas history drill-down. It reads local Copilot usage data in read-only mode and does not add external session deep links.
The preview below is rendered from the real canvas using entirely synthetic sample metrics, generic session names, generic agent names, and no local user data.
What's included
extensions/usage-insights/— pinned extension functionality fromdavidkaya/github-copilot-usage-insights@206d53faab1d8a3331de96e203d0206ad21845aa, adapted to theusage-insightsID and exact Usage Insights display name.plugins/usage-insights/— Agent Plugins v1.0.0 manifest registering./extensions/usage-insights, plus installation and privacy documentation.extensions/usage-insights/assets/preview.png— polished desktop preview generated with synthetic, sanitized sample data..github/plugin/marketplace.json,docs/README.plugins.md, and website data.Checklist
npm ci --registry=https://packagefeedproxy.microsoft.io/npmpassesnpm run plugin:validatepasses (usage-insightsvalid)npm run buildregenerates marketplace + docsnpm run website:dataregeneratedassets/preview.png)