From 6a72c95b9f40c7565a86173b0f0df97eec6f37e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=85=AC=E6=98=8E?=
<83812544+Ed1s0nZ@users.noreply.github.com>
Date: Fri, 26 Jun 2026 00:58:29 +0800
Subject: [PATCH] Add files via upload
---
web/static/i18n/en-US.json | 1 +
web/static/i18n/zh-CN.json | 1 +
web/static/js/dashboard.js | 50 +++----
web/static/js/monitor.js | 277 ++++++++++++++++++-------------------
4 files changed, 161 insertions(+), 168 deletions(-)
diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json
index 1f2b4556..eae3800d 100644
--- a/web/static/i18n/en-US.json
+++ b/web/static/i18n/en-US.json
@@ -1667,6 +1667,7 @@
"timelineSummary": "{{total}} calls in range · peak {{peak}}",
"timelineSparseHint": "Most buckets are empty; peak {{peak}} calls at {{peakTime}}",
"timelineNoData": "No calls in this period",
+ "timelineLoading": "Loading trend…",
"timelineEmptyHint": "Switch the time range or invoke MCP tools in chat or tasks",
"timelineLoadError": "Failed to load call trend",
"timelineTotalLegend": "Total calls",
diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json
index f40f8a03..0bbbec98 100644
--- a/web/static/i18n/zh-CN.json
+++ b/web/static/i18n/zh-CN.json
@@ -1655,6 +1655,7 @@
"timelineSummary": "区间内 {{total}} 次 · 峰值 {{peak}}",
"timelineSparseHint": "该时段多数时间为 0,峰值 {{peak}} 次出现在 {{peakTime}}",
"timelineNoData": "该时段暂无调用",
+ "timelineLoading": "趋势加载中…",
"timelineEmptyHint": "切换时间范围查看其他时段,或在对话/任务中调用 MCP 工具",
"timelineLoadError": "无法加载调用趋势",
"timelineTotalLegend": "总调用",
diff --git a/web/static/js/dashboard.js b/web/static/js/dashboard.js
index 2e1f4ee8..44b96454 100644
--- a/web/static/js/dashboard.js
+++ b/web/static/js/dashboard.js
@@ -118,7 +118,7 @@ async function refreshDashboard() {
fetchJson('/api/agent-loop/tasks'),
fetchJson('/api/vulnerabilities/stats'),
fetchJson('/api/batch-tasks?limit=500&page=1'),
- fetchJson('/api/monitor/stats'),
+ fetchJson('/api/monitor/stats?top=30'),
fetchJson('/api/knowledge/stats'),
fetchJson('/api/skills/stats'),
fetchJson('/api/vulnerabilities?limit=10&page=1'),
@@ -301,36 +301,27 @@ async function refreshDashboard() {
updateProgressBar('dashboard-batch-progress-done', '0');
}
- // 工具调用:monitor/stats 为 { toolName: { totalCalls, successCalls, failedCalls, ... } }
+ // 工具调用:monitor/stats 为 { summary, topTools }
let toolsCount = 0, toolsTotalCalls = 0, toolsSuccessRate = -1, toolsFailedCount = 0;
- if (monitorRes && typeof monitorRes === 'object') {
- const names = Object.keys(monitorRes);
- let totalCalls = 0, totalSuccess = 0, totalFailed = 0;
- names.forEach(k => {
- const v = monitorRes[k];
- const n = v && (v.totalCalls ?? v.TotalCalls);
- if (typeof n === 'number') totalCalls += n;
- const s = v && (v.successCalls ?? v.SuccessCalls);
- if (typeof s === 'number') totalSuccess += s;
- const f = v && (v.failedCalls ?? v.FailedCalls);
- if (typeof f === 'number') totalFailed += f;
- });
- toolsCount = names.length;
- toolsTotalCalls = totalCalls;
- toolsFailedCount = totalFailed;
- setEl('dashboard-kpi-tools-calls', formatNumber(totalCalls));
+ if (monitorRes && monitorRes.summary) {
+ const s = monitorRes.summary;
+ toolsCount = s.toolCount || 0;
+ toolsTotalCalls = s.totalCalls || 0;
+ toolsFailedCount = s.failedCalls || 0;
+ const totalSuccess = s.successCalls || 0;
+ setEl('dashboard-kpi-tools-calls', formatNumber(toolsTotalCalls));
setKpiSubText('dashboard-kpi-tools-sub-text',
dt('dashboard.toolsCountLabel', { count: toolsCount }, toolsCount + ' 个工具'));
- if (totalCalls > 0) {
- toolsSuccessRate = (totalSuccess / totalCalls) * 100;
+ if (toolsTotalCalls > 0) {
+ toolsSuccessRate = (totalSuccess / toolsTotalCalls) * 100;
const rateStr = toolsSuccessRate.toFixed(1) + '%';
setEl('dashboard-kpi-success-rate', rateStr);
- setKpiRateBadge('dashboard-kpi-rate-sub-text', toolsSuccessRate, totalFailed);
+ setKpiRateBadge('dashboard-kpi-rate-sub-text', toolsSuccessRate, toolsFailedCount);
} else {
setEl('dashboard-kpi-success-rate', '-');
setKpiSubText('dashboard-kpi-rate-sub-text', dt('dashboard.noCallYet', null, '暂无调用'));
}
- renderDashboardToolsBar(monitorRes);
+ renderDashboardToolsBar(monitorRes.topTools);
} else {
setEl('dashboard-kpi-tools-calls', '-');
setEl('dashboard-kpi-success-rate', '-');
@@ -1615,12 +1606,12 @@ function renderSeverityInsights(bySeverityOpen, totalOpen, recentVulnsRes) {
}
}
-function renderDashboardToolsBar(monitorRes) {
+function renderDashboardToolsBar(topTools) {
const placeholder = document.getElementById('dashboard-tools-pie-placeholder');
const barChartEl = document.getElementById('dashboard-tools-bar-chart');
if (!placeholder || !barChartEl) return;
- if (!monitorRes || typeof monitorRes !== 'object') {
+ if (!Array.isArray(topTools) || topTools.length === 0) {
placeholder.style.removeProperty('display');
placeholder.textContent = (typeof window.t === 'function' ? window.t('dashboard.noCallData') : '暂无调用数据');
barChartEl.style.display = 'none';
@@ -1628,11 +1619,12 @@ function renderDashboardToolsBar(monitorRes) {
return;
}
- const entries = Object.keys(monitorRes).map(function (k) {
- const v = monitorRes[k];
- const totalCalls = v && (v.totalCalls ?? v.TotalCalls);
- return { name: k, totalCalls: typeof totalCalls === 'number' ? totalCalls : 0 };
- }).filter(function (e) { return e.totalCalls > 0; })
+ const entries = topTools.map(function (t) {
+ return {
+ name: t.toolName || '',
+ totalCalls: typeof t.totalCalls === 'number' ? t.totalCalls : 0,
+ };
+ }).filter(function (e) { return e.name && e.totalCalls > 0; })
.sort(function (a, b) { return b.totalCalls - a.totalCalls; })
.slice(0, 30);
diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js
index a6c920ff..a1ac621d 100644
--- a/web/static/js/monitor.js
+++ b/web/static/js/monitor.js
@@ -3527,10 +3527,12 @@ let monitorPanelFetchSeq = 0;
// 监控面板状态
const monitorState = {
executions: [],
- stats: {},
+ summary: null,
+ topTools: [],
timeline: null,
timelineRange: null,
timelineError: null,
+ timelineLoading: false,
lastFetchedAt: null,
retentionDays: 0,
pagination: {
@@ -3626,17 +3628,14 @@ async function refreshMonitorPanel(page = null) {
try {
const mySeq = ++monitorPanelFetchSeq;
- // 如果指定了页码,使用指定页码,否则使用当前页码
const currentPage = page !== null ? page : monitorState.pagination.page;
const pageSize = monitorState.pagination.pageSize;
-
- // 获取当前的筛选条件
+
const statusFilter = document.getElementById('monitor-status-filter');
const toolFilter = document.getElementById('monitor-tool-filter');
const currentStatusFilter = statusFilter ? statusFilter.value : 'all';
const currentToolFilter = toolFilter ? (toolFilter.value.trim() || 'all') : 'all';
-
- // 构建请求 URL
+
let url = `/api/monitor?page=${currentPage}&page_size=${pageSize}`;
if (currentStatusFilter && currentStatusFilter !== 'all') {
url += `&status=${encodeURIComponent(currentStatusFilter)}`;
@@ -3644,37 +3643,34 @@ async function refreshMonitorPanel(page = null) {
if (currentToolFilter && currentToolFilter !== 'all') {
url += `&tool=${encodeURIComponent(currentToolFilter)}`;
}
-
- const { result, timeline, timelineError } = await fetchMonitorAndTimeline(url);
+
+ const range = getMcpMonitorTimelineRange();
+ monitorState.timelineLoading = true;
+ const timelinePromise = fetchMonitorTimeline(range);
+
+ const monitorResp = await apiFetch(url, { method: 'GET' });
+ const result = await monitorResp.json().catch(() => ({}));
+ if (!monitorResp.ok) {
+ throw new Error(result.error || '获取监控数据失败');
+ }
if (mySeq !== monitorPanelFetchSeq) {
return;
}
- monitorState.executions = Array.isArray(result.executions) ? result.executions : [];
- monitorState.stats = result.stats || {};
+ applyMonitorPayload(result, currentStatusFilter);
+
+ const { timeline, timelineError } = await timelinePromise;
+ if (mySeq !== monitorPanelFetchSeq) {
+ return;
+ }
monitorState.timeline = timeline;
monitorState.timelineError = timelineError;
- monitorState.lastFetchedAt = new Date();
- monitorState.retentionDays = typeof result.retention_days === 'number' ? result.retention_days : 0;
-
- // 更新分页信息
- if (result.total !== undefined) {
- monitorState.pagination = {
- page: result.page || currentPage,
- pageSize: result.page_size || pageSize,
- total: result.total || 0,
- totalPages: result.total_pages || 1
- };
- }
-
- renderMonitorStats(monitorState.stats, monitorState.lastFetchedAt);
- renderMonitorExecutions(monitorState.executions, currentStatusFilter);
- renderMonitorPagination();
-
- // 初始化每页显示数量选择器
+ monitorState.timelineLoading = false;
+ updateMonitorTimelineSection();
initializeMonitorPageSize();
} catch (error) {
console.error('刷新监控面板失败:', error);
+ monitorState.timelineLoading = false;
if (statsContainer) {
statsContainer.innerHTML = `
${escapeHtml(typeof window.t === 'function' ? window.t('mcpMonitor.loadStatsError') : '无法加载统计信息')}:${escapeHtml(error.message)}
`;
}
@@ -3717,10 +3713,9 @@ async function refreshMonitorPanelWithFilter(statusFilter = 'all', toolFilter =
try {
const mySeq = ++monitorPanelFetchSeq;
- const currentPage = 1; // 筛选时重置到第一页
+ const currentPage = 1;
const pageSize = monitorState.pagination.pageSize;
-
- // 构建请求 URL
+
let url = `/api/monitor?page=${currentPage}&page_size=${pageSize}`;
if (statusFilter && statusFilter !== 'all') {
url += `&status=${encodeURIComponent(statusFilter)}`;
@@ -3728,37 +3723,34 @@ async function refreshMonitorPanelWithFilter(statusFilter = 'all', toolFilter =
if (toolFilter && toolFilter !== 'all') {
url += `&tool=${encodeURIComponent(toolFilter)}`;
}
-
- const { result, timeline, timelineError } = await fetchMonitorAndTimeline(url);
+
+ const range = getMcpMonitorTimelineRange();
+ monitorState.timelineLoading = true;
+ const timelinePromise = fetchMonitorTimeline(range);
+
+ const monitorResp = await apiFetch(url, { method: 'GET' });
+ const result = await monitorResp.json().catch(() => ({}));
+ if (!monitorResp.ok) {
+ throw new Error(result.error || '获取监控数据失败');
+ }
if (mySeq !== monitorPanelFetchSeq) {
return;
}
- monitorState.executions = Array.isArray(result.executions) ? result.executions : [];
- monitorState.stats = result.stats || {};
+ applyMonitorPayload(result, statusFilter);
+
+ const { timeline, timelineError } = await timelinePromise;
+ if (mySeq !== monitorPanelFetchSeq) {
+ return;
+ }
monitorState.timeline = timeline;
monitorState.timelineError = timelineError;
- monitorState.lastFetchedAt = new Date();
- monitorState.retentionDays = typeof result.retention_days === 'number' ? result.retention_days : 0;
-
- // 更新分页信息
- if (result.total !== undefined) {
- monitorState.pagination = {
- page: result.page || currentPage,
- pageSize: result.page_size || pageSize,
- total: result.total || 0,
- totalPages: result.total_pages || 1
- };
- }
-
- renderMonitorStats(monitorState.stats, monitorState.lastFetchedAt);
- renderMonitorExecutions(monitorState.executions, statusFilter);
- renderMonitorPagination();
-
- // 初始化每页显示数量选择器
+ monitorState.timelineLoading = false;
+ updateMonitorTimelineSection();
initializeMonitorPageSize();
} catch (error) {
console.error('刷新监控面板失败:', error);
+ monitorState.timelineLoading = false;
if (statsContainer) {
statsContainer.innerHTML = `${escapeHtml(typeof window.t === 'function' ? window.t('mcpMonitor.loadStatsError') : '无法加载统计信息')}:${escapeHtml(error.message)}
`;
}
@@ -3768,6 +3760,63 @@ async function refreshMonitorPanelWithFilter(statusFilter = 'all', toolFilter =
}
}
+function applyMonitorPayload(result, statusFilter) {
+ const currentPage = monitorState.pagination.page;
+ const pageSize = monitorState.pagination.pageSize;
+
+ monitorState.executions = Array.isArray(result.executions) ? result.executions : [];
+ monitorState.summary = result.summary || null;
+ monitorState.topTools = Array.isArray(result.topTools) ? result.topTools : [];
+ monitorState.lastFetchedAt = new Date();
+ monitorState.retentionDays = typeof result.retentionDays === 'number' ? result.retentionDays : 0;
+
+ if (result.total !== undefined) {
+ monitorState.pagination = {
+ page: result.page || currentPage,
+ pageSize: result.pageSize || pageSize,
+ total: result.total || 0,
+ totalPages: result.totalPages || 1
+ };
+ }
+
+ renderMonitorStats(monitorState.summary, monitorState.topTools, monitorState.lastFetchedAt);
+ renderMonitorExecutions(monitorState.executions, statusFilter);
+ renderMonitorPagination();
+}
+
+async function fetchMonitorTimeline(range) {
+ try {
+ const timelineResp = await apiFetch(`/api/monitor/calls-timeline?range=${encodeURIComponent(range)}`, { method: 'GET' });
+ const timelineJson = await timelineResp.json().catch(() => ({}));
+ if (!timelineResp.ok) {
+ return { timeline: null, timelineError: timelineJson.error || 'timeline failed' };
+ }
+ return { timeline: timelineJson, timelineError: null };
+ } catch (err) {
+ return { timeline: null, timelineError: err && err.message ? err.message : 'timeline failed' };
+ }
+}
+
+function updateMonitorTimelineSection() {
+ const timelineInner = document.querySelector('#monitor-stats .mcp-stats-combined__timeline-inner');
+ if (timelineInner) {
+ const combined = timelineInner.closest('.mcp-stats-combined');
+ const compactEmpty = combined && !!combined.querySelector('.mcp-stats-combined__main');
+ timelineInner.innerHTML = renderMcpStatsTimelineBody(
+ monitorState.timeline,
+ monitorState.timelineError,
+ compactEmpty,
+ monitorState.timelineLoading
+ );
+ bindMcpStatsTimelineEvents();
+ syncMcpMonitorTimelineRangeUI();
+ return;
+ }
+ if (monitorState.summary) {
+ renderMonitorStats(monitorState.summary, monitorState.topTools, monitorState.lastFetchedAt);
+ }
+}
+
const MCP_STATS_TOP_N = 6;
const MCP_TIMELINE_RANGES = ['24h', '7d', '30d'];
@@ -3782,29 +3831,14 @@ function getMcpMonitorTimelineRange() {
return range;
}
-async function fetchMonitorAndTimeline(monitorUrl) {
- const range = getMcpMonitorTimelineRange();
- const [monitorResp, timelineResp] = await Promise.all([
- apiFetch(monitorUrl, { method: 'GET' }),
- apiFetch(`/api/monitor/calls-timeline?range=${encodeURIComponent(range)}`, { method: 'GET' })
- ]);
- const result = await monitorResp.json().catch(() => ({}));
- if (!monitorResp.ok) {
- throw new Error(result.error || '获取监控数据失败');
- }
- let timeline = null;
- let timelineError = null;
- try {
- const timelineJson = await timelineResp.json().catch(() => ({}));
- if (timelineResp.ok) {
- timeline = timelineJson;
- } else {
- timelineError = timelineJson.error || 'timeline failed';
- }
- } catch (err) {
- timelineError = err && err.message ? err.message : 'timeline failed';
- }
- return { result, timeline, timelineError };
+function buildMonitorTotals(summary) {
+ const s = summary && typeof summary === 'object' ? summary : {};
+ return {
+ total: s.totalCalls || 0,
+ success: s.successCalls || 0,
+ failed: s.failedCalls || 0,
+ lastCallTime: s.lastCallTime ? new Date(s.lastCallTime) : null,
+ };
}
function formatMcpTimelineLabel(isoOrDate, rangeKey, locale) {
@@ -4028,34 +4062,19 @@ async function setMcpMonitorTimelineRange(range) {
localStorage.setItem('mcpMonitorTimelineRange', range);
monitorState.timelineRange = range;
monitorState.timelineError = null;
+ monitorState.timelineLoading = true;
syncMcpMonitorTimelineRangeUI(range);
+ updateMonitorTimelineSection();
try {
- const timelineResp = await apiFetch(`/api/monitor/calls-timeline?range=${encodeURIComponent(range)}`, { method: 'GET' });
- const timelineJson = await timelineResp.json().catch(() => ({}));
- if (!timelineResp.ok) {
- throw new Error(timelineJson.error || '加载趋势失败');
- }
- monitorState.timeline = timelineJson;
- const timelineInner = document.querySelector('#monitor-stats .mcp-stats-combined__timeline-inner');
- if (timelineInner) {
- const combined = timelineInner.closest('.mcp-stats-combined');
- const compactEmpty = combined && !!combined.querySelector('.mcp-stats-combined__main');
- timelineInner.innerHTML = renderMcpStatsTimelineBody(monitorState.timeline, monitorState.timelineError, compactEmpty);
- bindMcpStatsTimelineEvents();
- syncMcpMonitorTimelineRangeUI(range);
- } else if (monitorState.stats && Object.keys(monitorState.stats).length > 0) {
- renderMonitorStats(monitorState.stats, monitorState.lastFetchedAt);
- }
+ const { timeline, timelineError } = await fetchMonitorTimeline(range);
+ monitorState.timeline = timeline;
+ monitorState.timelineError = timelineError;
+ monitorState.timelineLoading = false;
+ updateMonitorTimelineSection();
} catch (err) {
monitorState.timelineError = err.message || 'error';
- const timelineInner = document.querySelector('#monitor-stats .mcp-stats-combined__timeline-inner');
- if (timelineInner) {
- const combined = timelineInner.closest('.mcp-stats-combined');
- const compactEmpty = combined && !!combined.querySelector('.mcp-stats-combined__main');
- timelineInner.innerHTML = renderMcpStatsTimelineBody(monitorState.timeline, monitorState.timelineError, compactEmpty);
- bindMcpStatsTimelineEvents();
- syncMcpMonitorTimelineRangeUI(range);
- }
+ monitorState.timelineLoading = false;
+ updateMonitorTimelineSection();
}
}
window.setMcpMonitorTimelineRange = setMcpMonitorTimelineRange;
@@ -4084,7 +4103,12 @@ function renderMcpStatsTimelineEmptyState(compact) {
`;
}
-function renderMcpStatsTimelineBody(timeline, timelineError, compactEmpty) {
+function renderMcpStatsTimelineBody(timeline, timelineError, compactEmpty, loading) {
+ if (loading) {
+ const loadingText = mcpMonitorT('timelineLoading') || monitorFallback('趋势加载中…', 'Loading trend…');
+ return `${escapeHtml(loadingText)}
`;
+ }
+
const hint = mcpMonitorT('timelineHint') || monitorFallback('全部工具合计', 'All tools combined');
if (timelineError) {
@@ -4152,7 +4176,7 @@ function renderMcpStatsCombinedSection(topTools, totals, activeToolFilter, timel
const timelineCol = showTimeline
? `
${escapeHtml(timelineTitle)}
-
${renderMcpStatsTimelineBody(timeline, timelineError, hasTools)}
+
${renderMcpStatsTimelineBody(timeline, timelineError, hasTools, monitorState.timelineLoading)}
`
: '';
@@ -4207,20 +4231,11 @@ function refreshMonitorPanelFromState() {
if (!monitorState.lastFetchedAt) return;
const statusFilter = document.getElementById('monitor-status-filter');
const currentStatusFilter = statusFilter ? statusFilter.value : 'all';
- renderMonitorStats(monitorState.stats || {}, monitorState.lastFetchedAt);
+ renderMonitorStats(monitorState.summary, monitorState.topTools, monitorState.lastFetchedAt);
renderMonitorExecutions(monitorState.executions || [], currentStatusFilter);
renderMonitorPagination();
}
-function normalizeMonitorStatsEntries(statsMap) {
- if (!statsMap || typeof statsMap !== 'object') return [];
- return Object.entries(statsMap).map(([key, item]) => {
- const stat = item && typeof item === 'object' ? { ...item } : {};
- if (!stat.toolName) stat.toolName = key;
- return stat;
- });
-}
-
const MCP_STATS_TOOL_CHEVRON = '';
function getMcpStatsRateTone(rateNum) {
@@ -4915,15 +4930,19 @@ function renderMcpStatsToolRanking(topTools, totals, activeToolFilter = '', opti
return renderMcpStatsDetailSection(topTools, totals, activeToolFilter);
}
-function renderMonitorStats(statsMap = {}, lastFetchedAt = null) {
+function renderMonitorStats(summary = null, topTools = [], lastFetchedAt = null) {
const container = document.getElementById('monitor-stats');
if (!container) {
return;
}
- const entries = normalizeMonitorStatsEntries(statsMap);
- const showTimeline = monitorState.timeline != null || !!monitorState.timelineError;
- if (entries.length === 0 && !showTimeline) {
+ const tools = Array.isArray(topTools) ? topTools : [];
+ const totals = buildMonitorTotals(summary);
+ const toolCount = summary && typeof summary.toolCount === 'number' ? summary.toolCount : tools.length;
+ const showTimeline = monitorState.timelineLoading || monitorState.timeline != null || !!monitorState.timelineError;
+ const hasSummaryData = toolCount > 0 || totals.total > 0;
+
+ if (!hasSummaryData && !showTimeline) {
const noStats = mcpMonitorT('noStatsData') || monitorFallback('暂无统计数据', 'No statistical data');
container.innerHTML = '' + escapeHtml(noStats) + '
';
const subtitle = document.getElementById('monitor-stats-subtitle');
@@ -4931,20 +4950,6 @@ function renderMonitorStats(statsMap = {}, lastFetchedAt = null) {
return;
}
- const totals = entries.reduce(
- (acc, item) => {
- acc.total += item.totalCalls || 0;
- acc.success += item.successCalls || 0;
- acc.failed += item.failedCalls || 0;
- const lastCall = item.lastCallTime ? new Date(item.lastCallTime) : null;
- if (lastCall && (!acc.lastCallTime || lastCall > acc.lastCallTime)) {
- acc.lastCallTime = lastCall;
- }
- return acc;
- },
- { total: 0, success: 0, failed: 0, lastCallTime: null }
- );
-
const hasCalls = totals.total > 0;
const successRateNum = hasCalls ? (totals.success / totals.total) * 100 : 0;
const successRate = hasCalls ? successRateNum.toFixed(1) : '-';
@@ -4965,19 +4970,13 @@ function renderMonitorStats(statsMap = {}, lastFetchedAt = null) {
const toolFilterEl = document.getElementById('monitor-tool-filter');
const activeToolFilter = toolFilterEl ? toolFilterEl.value.trim() : '';
- const topTools = entries
- .filter(tool => (tool.totalCalls || 0) > 0)
- .slice()
- .sort((a, b) => (b.totalCalls || 0) - (a.totalCalls || 0))
- .slice(0, MCP_STATS_TOP_N);
-
const hasAnyCalls = totals.total > 0;
- const showCombined = hasAnyCalls && (topTools.length > 0 || showTimeline);
+ const showCombined = hasAnyCalls && (tools.length > 0 || showTimeline);
const html = `
${renderMcpStatsMetricsBar(totals, successRate, rateTone, rateSubText, lastCallText, hasCalls)}
${showCombined ? renderMcpStatsCombinedSection(
- topTools,
+ tools,
totals,
activeToolFilter,
monitorState.timeline,
@@ -4995,7 +4994,7 @@ function renderMonitorStats(statsMap = {}, lastFetchedAt = null) {
} else if (toolFilterEl) {
toolFilterEl.classList.remove('is-filter-active');
}
- updateMonitorStatsSubtitle(lastFetchedAt, entries.length, monitorState.retentionDays);
+ updateMonitorStatsSubtitle(lastFetchedAt, toolCount, monitorState.retentionDays);
}
function renderMonitorExecutions(executions = [], statusFilter = 'all') {