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
8 changes: 8 additions & 0 deletions vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,14 @@
"type": "string",
"default": "",
"description": "Screenshot/demo mode: when set to a folder path, overrides all session file scanning and returns only .json/.jsonl files from this directory. Leave empty for normal operation."
},
"copilotTokenTracker.suppressedUnknownTools": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"description": "List of tool names to suppress from the 'Unknown Tools Found' section in the Usage Analysis dashboard. Useful for tools you are testing that don't yet have friendly display names."
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3837,6 +3837,19 @@ class CopilotTokenTracker implements vscode.Disposable {
vscode.commands.executeCommand('workbench.action.chat.open', { query: message.prompt, isNewChat: true })
);
break;
case 'suppressUnknownTool': {
const toolName = message.toolName as string;
if (toolName) {
const config = vscode.workspace.getConfiguration('copilotTokenTracker');
const current = config.get<string[]>('suppressedUnknownTools', []);
if (!current.includes(toolName)) {
await config.update('suppressedUnknownTools', [...current, toolName], vscode.ConfigurationTarget.Global);
this.log(`🔇 Suppressed unknown tool: ${toolName}`);
}
await this.dispatch('refresh:analysis', () => this.refreshAnalysisPanel());
}
break;
}
}
});

Expand Down Expand Up @@ -7068,6 +7081,10 @@ ${hashtag}`;
`[Usage Analysis] Test format 1234567.89: ${new Intl.NumberFormat(detectedLocale).format(1234567.89)}`,
);

const suppressedUnknownTools = vscode.workspace
.getConfiguration('copilotTokenTracker')
.get<string[]>('suppressedUnknownTools', []);

const initialData = JSON.stringify({
today: stats.today,
last30Days: stats.last30Days,
Expand All @@ -7078,6 +7095,7 @@ ${hashtag}`;
lastUpdated: stats.lastUpdated.toISOString(),
backendConfigured: this.isBackendConfigured(),
currentWorkspacePaths: vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath) ?? [],
suppressedUnknownTools,
}).replace(/</g, "\\u003c");

return `<!DOCTYPE html>
Expand Down
19 changes: 16 additions & 3 deletions vscode-extension/src/webview/usage/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type UsageAnalysisStats = {
missedPotential?: MissedPotentialWorkspace[];
backendConfigured?: boolean;
currentWorkspacePaths?: string[];
suppressedUnknownTools?: string[];
};

declare function acquireVsCodeApi<TState = unknown>(): {
Expand Down Expand Up @@ -159,9 +160,11 @@ function getUnknownMcpTools(stats: UsageAnalysisStats): string[] {
Object.entries(stats.today.toolCalls.byTool).forEach(([tool]) => allTools.add(tool));
Object.entries(stats.last30Days.toolCalls.byTool).forEach(([tool]) => allTools.add(tool));
Object.entries(stats.month.toolCalls.byTool).forEach(([tool]) => allTools.add(tool));

const suppressed = new Set<string>(stats.suppressedUnknownTools ?? []);

// Filter to only unknown tools (where lookupToolName returns the same value)
return Array.from(allTools).filter(tool => lookupToolName(tool) === tool).sort();
// Filter to only unknown tools (where lookupToolName returns the same value) and not suppressed
return Array.from(allTools).filter(tool => lookupToolName(tool) === tool && !suppressed.has(tool)).sort();
}

function createMcpToolIssueUrl(unknownTools: string[]): string {
Expand Down Expand Up @@ -948,7 +951,8 @@ function renderLayout(stats: UsageAnalysisStats): void {
if (last30Count > todayCount) { countParts.push(`${last30Count} in the last 30d`); }
if (monthCount > last30Count) { countParts.push(`${monthCount} this month`); }
const countHtml = countParts.length > 0 ? `<span style="color:var(--text-muted);"> (${countParts.join(' | ')})</span>` : '';
return `<span style="display:inline-flex; align-items:center; gap:4px; padding:2px 6px; background:var(--bg-primary); border:1px solid var(--border-color); border-radius:3px; font-family:monospace; font-size:11px;">${escapeHtml(tool)}${countHtml}</span>`;
const suppressBtn = `<button data-suppress-tool="${escapeHtml(tool)}" title="Suppress this tool from the unknown list" style="background:none; border:none; cursor:pointer; padding:0 2px; color:var(--text-muted); font-size:11px; line-height:1;" aria-label="Suppress ${escapeHtml(tool)}">🔇</button>`;
return `<span style="display:inline-flex; align-items:center; gap:4px; padding:2px 6px; background:var(--bg-primary); border:1px solid var(--border-color); border-radius:3px; font-family:monospace; font-size:11px;">${escapeHtml(tool)}${countHtml}${suppressBtn}</span>`;
}).join(' ');
return `
<div id="unknown-mcp-tools-section" style="margin-bottom: 12px; padding: 10px; background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 6px;">
Expand Down Expand Up @@ -1702,6 +1706,15 @@ async function bootstrap(): Promise<void> {
console.log('[Usage Analysis] Test format 1234567.89 with received locale:', new Intl.NumberFormat(initialData.locale).format(1234567.89));
setFormatLocale(initialData.locale);
renderLayout(initialData);

// Event delegation for suppress-tool buttons (rendered dynamically in the tools section)
document.addEventListener('click', (event) => {
const target = event.target as HTMLElement;
const toolName = target.getAttribute('data-suppress-tool');
if (toolName) {
vscode.postMessage({ command: 'suppressUnknownTool', toolName });
}
});
}

void bootstrap();
Expand Down
Loading