feat(ai): 新增代码热点诊断探针

- 注册 inspect_codebase_hotspots 内置工具并接入本地执行器

- 在工具目录、系统提示和斜杠菜单中暴露大文件治理入口

- 增加工具注册、执行器、目录和斜杠菜单测试
This commit is contained in:
Syngnat
2026-06-12 02:30:09 +08:00
parent 4daaa22cba
commit 453e13c88d
14 changed files with 307 additions and 1 deletions

View File

@@ -104,6 +104,9 @@ describe('AIBuiltinToolsCatalog', () => {
expect(markup).toContain('诊断 AI 上下文体量');
expect(markup).toContain('inspect_ai_context_budget');
expect(markup).toContain('messageLimit');
expect(markup).toContain('治理前端大文件');
expect(markup).toContain('inspect_codebase_hotspots');
expect(markup).toContain('大文件和拆分热点');
expect(markup).toContain('复用历史 SQL');
expect(markup).toContain('inspect_saved_queries');
expect(markup).toContain('回看 AI 历史对话');

View File

@@ -0,0 +1,180 @@
export interface CodebaseHotspotEntry {
path: string;
lines: number;
area: string;
riskLevel: 'medium' | 'high' | 'critical';
why: string;
suggestedSlices: string[];
testTargets: string[];
}
export interface CodebaseHotspotSnapshotOptions {
keyword?: string;
minLines?: number;
limit?: number;
includeRecommendations?: boolean;
}
const CODEBASE_HOTSPOT_SNAPSHOT: CodebaseHotspotEntry[] = [
{
path: 'frontend/src/components/Sidebar.tsx',
lines: 8910,
area: 'workspace-navigation',
riskLevel: 'critical',
why: '左侧树、命令面板、上下文菜单和连接动作集中在单文件,修改入口多且回归面大。',
suggestedSlices: ['V2 命令面板', '外部 SQL 目录弹窗', '连接树动作', '批量操作弹窗'],
testTargets: ['Sidebar.locate-toolbar.test.tsx', 'sidebarV2Utils.test.ts'],
},
{
path: 'frontend/src/components/DataGrid.tsx',
lines: 8080,
area: 'result-grid',
riskLevel: 'critical',
why: '结果展示、编辑、DDL、导出和列操作耦合容易让单点修复影响查询结果区。',
suggestedSlices: ['结果导出工具栏', '列头菜单', 'DDL 视图', '单元格编辑事务提示'],
testTargets: ['DataGrid.layout.test.tsx', 'DataGrid.ddl.test.tsx'],
},
{
path: 'frontend/src/components/ConnectionModal.tsx',
lines: 7462,
area: 'connection-form',
riskLevel: 'critical',
why: '多数据源连接表单仍集中在一个组件,新增数据源或密钥规则时容易互相影响。',
suggestedSlices: ['SSH/代理配置区', 'TLS 配置区', 'MongoDB 配置区', 'JVM 配置区'],
testTargets: ['ConnectionModal.edit-password.test.tsx', 'connectionModalPresentation.test.ts'],
},
{
path: 'frontend/src/components/QueryEditor.tsx',
lines: 5367,
area: 'sql-editor',
riskLevel: 'critical',
why: 'SQL 编辑、执行、事务、结果区布局和快捷键状态集中,事务和结果区回归风险高。',
suggestedSlices: ['结果区工具栏', '事务状态条', '执行日志提示', '编辑器快捷键绑定'],
testTargets: ['QueryEditor.result-panel.test.tsx', 'useSqlEditorTransactionController.test.ts'],
},
{
path: 'frontend/src/components/TableDesigner.tsx',
lines: 3549,
area: 'table-designer',
riskLevel: 'high',
why: '字段编辑、索引、外键、分区和 DDL 生成集中,数据库方言差异容易扩散。',
suggestedSlices: ['字段编辑表格', '索引配置面板', '外键配置面板', '方言 DDL 预览'],
testTargets: ['TableDesigner.*.test.tsx', 'tableDesignerSchemaSql.test.ts'],
},
{
path: 'frontend/src/components/RedisViewer.tsx',
lines: 2120,
area: 'redis-browser',
riskLevel: 'high',
why: 'Key 浏览、不同数据结构编辑、TTL、编码显示和新增弹窗集中Redis Cluster/Sentinel 后续验证面较宽。',
suggestedSlices: ['Key 搜索栏', 'String/List/Set/ZSet/Hash/Stream 编辑器', '新增 Key 弹窗'],
testTargets: ['redisViewerTree.test.ts', 'RedisViewer.*.test.tsx'],
},
{
path: 'frontend/src/components/DriverManagerModal.tsx',
lines: 1729,
area: 'driver-manager',
riskLevel: 'high',
why: '驱动安装、状态展示、下载和可选代理逻辑较多,适合继续拆出状态卡片和操作区。',
suggestedSlices: ['驱动状态列表', '安装操作区', '下载日志区'],
testTargets: ['DriverManagerModal.*.test.tsx'],
},
{
path: 'frontend/src/components/DataSyncModal.tsx',
lines: 1526,
area: 'data-sync',
riskLevel: 'high',
why: '数据同步连接、表映射、预检查和执行结果集中,数据库方言问题容易隐藏。',
suggestedSlices: ['连接选择区', '表映射区', '同步预检查结果', '执行日志区'],
testTargets: ['DataSyncModal.*.test.tsx'],
},
{
path: 'frontend/src/components/JVMDiagnosticConsole.tsx',
lines: 1146,
area: 'jvm-diagnostics',
riskLevel: 'medium',
why: '诊断命令、输出块、权限提示和会话状态可继续拆分,降低 JVM 诊断回归面。',
suggestedSlices: ['命令输入区', '诊断输出区', '权限提示区'],
testTargets: ['JVMDiagnosticConsole.*.test.tsx'],
},
];
const normalizeKeyword = (value: unknown): string => String(value || '').trim().toLowerCase();
const clampNumber = (value: unknown, fallback: number, min: number, max: number): number => {
const parsed = Number(value);
if (!Number.isFinite(parsed)) {
return fallback;
}
return Math.max(min, Math.min(max, Math.floor(parsed)));
};
const matchesKeyword = (entry: CodebaseHotspotEntry, keyword: string): boolean => {
if (!keyword) {
return true;
}
return [
entry.path,
entry.area,
entry.riskLevel,
entry.why,
...entry.suggestedSlices,
...entry.testTargets,
].some((item) => item.toLowerCase().includes(keyword));
};
export const buildCodebaseHotspotSnapshot = ({
keyword,
minLines,
limit,
includeRecommendations = true,
}: CodebaseHotspotSnapshotOptions = {}) => {
const normalizedKeyword = normalizeKeyword(keyword);
const normalizedMinLines = clampNumber(minLines, 1000, 1, 20000);
const normalizedLimit = clampNumber(limit, 8, 1, 30);
const matched = CODEBASE_HOTSPOT_SNAPSHOT
.filter((entry) => entry.lines >= normalizedMinLines)
.filter((entry) => matchesKeyword(entry, normalizedKeyword))
.slice(0, normalizedLimit);
const criticalCount = matched.filter((entry) => entry.riskLevel === 'critical').length;
const highCount = matched.filter((entry) => entry.riskLevel === 'high').length;
return {
kind: 'codebase_hotspots',
source: 'static_maintainability_snapshot',
evidence: {
measuredAt: '2026-06-12',
note: '基于当前仓库前端文件行数热点快照;提交前仍应用 git diff 和定向测试核对具体改动。',
},
filters: {
keyword: normalizedKeyword || undefined,
minLines: normalizedMinLines,
limit: normalizedLimit,
includeRecommendations,
},
summary: {
totalKnownHotspots: CODEBASE_HOTSPOT_SNAPSHOT.length,
totalMatched: matched.length,
maxLines: matched[0]?.lines || 0,
criticalCount,
highCount,
},
hotspots: matched.map((entry, index) => ({
rank: index + 1,
path: entry.path,
lines: entry.lines,
area: entry.area,
riskLevel: entry.riskLevel,
why: entry.why,
suggestedSlices: includeRecommendations ? entry.suggestedSlices : [],
testTargets: includeRecommendations ? entry.testTargets : [],
})),
nextActions: includeRecommendations
? [
'优先选择已有测试覆盖的 slice 做小步拆分,避免直接重写整个大组件。',
'拆分后至少运行对应组件测试、相关 utils 测试和 npm --prefix frontend run build。',
'涉及可见 UI 的拆分需要用浏览器打开真实页面做一次冒烟验证。',
]
: [],
};
};

View File

@@ -0,0 +1,52 @@
import { describe, expect, it, vi } from 'vitest';
import type { AIToolCall, SavedConnection } from '../../types';
import { executeLocalAIToolCall } from './aiLocalToolExecutor';
const buildToolCall = (name: string, args: Record<string, unknown>): AIToolCall => ({
id: `call-${name}`,
type: 'function',
function: {
name,
arguments: JSON.stringify(args),
},
});
const buildConnection = (): SavedConnection => ({
id: 'conn-1',
name: '本地开发库',
config: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
user: 'root',
},
});
describe('aiLocalToolExecutor inspect_codebase_hotspots', () => {
it('returns frontend large-file hotspots and refactor test targets without source content', async () => {
const result = await executeLocalAIToolCall({
toolCall: buildToolCall('inspect_codebase_hotspots', {
keyword: 'QueryEditor',
minLines: 1000,
limit: 5,
}),
connections: [buildConnection()],
mcpTools: [],
toolContextMap: new Map(),
runtime: {
getDatabases: vi.fn(),
getTables: vi.fn(),
},
});
expect(result.success).toBe(true);
expect(result.toolName).toBe('inspect_codebase_hotspots');
expect(result.content).toContain('"kind":"codebase_hotspots"');
expect(result.content).toContain('frontend/src/components/QueryEditor.tsx');
expect(result.content).toContain('"riskLevel":"critical"');
expect(result.content).toContain('事务状态条');
expect(result.content).toContain('QueryEditor.result-panel.test.tsx');
expect(result.content).not.toContain('import React');
});
});

View File

@@ -14,6 +14,7 @@ describe('aiSlashCommands', () => {
expect(commands.some((command) => command.cmd === '/health')).toBe(true);
expect(commands.some((command) => command.cmd === '/tools')).toBe(true);
expect(commands.some((command) => command.cmd === '/budget')).toBe(true);
expect(commands.some((command) => command.cmd === '/hotspots')).toBe(true);
expect(commands.some((command) => command.cmd === '/mcp')).toBe(true);
expect(commands.some((command) => command.cmd === '/mcpfail')).toBe(true);
expect(commands.some((command) => command.cmd === '/mcpadd')).toBe(true);
@@ -45,6 +46,12 @@ describe('aiSlashCommands', () => {
expect(filterAISlashCommands('/bud').map((command) => command.cmd)).toContain('/budget');
});
it('supports filtering code hotspot diagnostics by keyword and command prefix', () => {
expect(filterAISlashCommands('大文件').map((command) => command.cmd)).toContain('/hotspots');
expect(filterAISlashCommands('拆分').map((command) => command.cmd)).toContain('/hotspots');
expect(filterAISlashCommands('/hot').map((command) => command.cmd)).toContain('/hotspots');
});
it('supports filtering shortcut diagnostics by chinese keyword and command prefix', () => {
expect(filterAISlashCommands('快捷键').map((command) => command.cmd)).toContain('/shortcuts');
expect(filterAISlashCommands('/sho').map((command) => command.cmd)).toContain('/shortcuts');

View File

@@ -50,6 +50,7 @@ export const DEFAULT_AI_SLASH_COMMANDS: AISlashCommandDefinition[] = [
{ cmd: '/health', label: '🩺 AI 配置体检', desc: '调用体检探针总览当前 AI 配置', prompt: '请先调用 inspect_ai_setup_health对当前 GoNavi AI 配置做一次完整体检,然后总结 blockers、warnings 和 nextActions。', category: 'diagnose', featured: true, keywords: ['health', '体检', 'ai配置', '探针'] },
{ cmd: '/tools', label: '🧰 工具目录', desc: '按关键词选择该用哪个内置探针', prompt: '请先调用 inspect_ai_tool_catalog按我的问题关键词筛选推荐流程、内置工具参数提示和当前 MCP 工具摘要,再告诉我下一步应该调用哪个工具。关键词:', category: 'diagnose', keywords: ['工具目录', '内置工具', 'toolcatalog', '参数提示', 'arguments', '探针路线'] },
{ cmd: '/budget', label: '🧠 上下文体量', desc: '诊断消息、DDL、MCP schema 和 Skills 体量', prompt: '请先调用 inspect_ai_context_budget检查当前会话消息、工具结果、DDL、MCP schema、提示词和 Skills 的体量风险,并给出应该收窄哪些上下文。', category: 'diagnose', keywords: ['上下文', 'context', '体量', '预算', '变慢', '乱答', 'schema太大', '工具结果'] },
{ cmd: '/hotspots', label: '🧱 代码热点', desc: '查看大文件拆分候选和测试范围', prompt: '请先调用 inspect_codebase_hotspots读取当前 GoNavi 前端大文件热点、建议拆分切片和测试目标,再告诉我下一步最适合拆哪个文件、拆到什么边界,以及需要跑哪些验证。关键词:', category: 'diagnose', keywords: ['大文件', '臃肿', '拆分', '重构', 'hotspots', '代码热点', '几千行'] },
{ cmd: '/mcp', label: '🪛 排查 MCP 接入', desc: '检查 MCP 服务和外部客户端状态', prompt: '请先调用 inspect_mcp_setup帮我盘点当前 MCP 服务、工具发现结果,以及 Claude Code / Codex 本机客户端和 OpenClaw / Hermans 远程 Agent 的接入状态。', category: 'diagnose', featured: true, keywords: ['mcp', 'codex', 'claude', 'openclaw', 'hermans', '外部客户端'] },
{ cmd: '/mcpfail', label: '🧯 MCP 运行失败', desc: '读取 MCP 启动、发现和调用失败日志', prompt: '请先调用 inspect_mcp_runtime_failures读取最近 MCP 启动、工具发现、工具调用、stdio、Docker 或 HTTP MCP 失败日志,结合当前 MCP 服务配置判断原因和 nextActions。关键词或服务名', category: 'diagnose', keywords: ['mcpfail', 'mcp失败', '运行期失败', '工具发现0个', 'stdio', 'docker mcp', 'http mcp', '启动失败', '调用失败'] },
{ cmd: '/mcpadd', label: '🧭 新增 MCP 指引', desc: '查看 command、args、env 和模板怎么填', prompt: '请先调用 inspect_mcp_authoring_guide如果我贴了完整启动命令或草稿再调用 inspect_mcp_draft 试算字段和校验问题;最后结合 inspect_mcp_setup告诉我新增 GoNavi MCP 服务时 command、args、env、timeout 应该怎么填,以及最接近的模板应该选哪个。', category: 'diagnose', featured: true, keywords: ['mcp新增', 'command', 'args', 'env', '模板'] },

View File

@@ -15,6 +15,7 @@ import {
buildAIMessageFlowSnapshot,
} from './aiChatSessionInsights';
import { buildAIContextBudgetSnapshot } from './aiContextBudgetInsights';
import { buildCodebaseHotspotSnapshot } from './aiCodebaseHotspotInsights';
import {
buildSavedQueriesSnapshot,
buildSqlSnippetsSnapshot,
@@ -114,6 +115,16 @@ export async function executeDiagnosticsSnapshotToolCall({
})),
success: true,
};
case 'inspect_codebase_hotspots':
return {
content: JSON.stringify(buildCodebaseHotspotSnapshot({
keyword: args.keyword,
minLines: args.minLines,
limit: args.limit,
includeRecommendations: args.includeRecommendations !== false,
})),
success: true,
};
case 'inspect_recent_sql_logs':
return {
content: JSON.stringify(buildRecentSqlLogsSnapshot({

View File

@@ -180,6 +180,7 @@ export async function executeSnapshotInspectionToolCall(
inspect_ai_last_render_error: '读取最近一次 AI 渲染异常失败',
inspect_ai_message_flow: '读取 AI 消息流诊断失败',
inspect_ai_context_budget: '读取 AI 上下文体量诊断失败',
inspect_codebase_hotspots: '读取代码热点诊断失败',
inspect_saved_queries: '读取已保存查询失败',
inspect_sql_snippets: '读取 SQL 片段失败',
inspect_shortcuts: '读取快捷键配置失败',

View File

@@ -68,7 +68,7 @@ describe('buildAISystemContextMessages', () => {
connections: [connections[0]],
tabs: [],
activeTabId: null,
availableToolNames: ['inspect_workspace_tabs', 'inspect_app_health', 'inspect_ai_support_bundle', 'inspect_ai_setup_health', 'inspect_ai_runtime', 'inspect_ai_safety', 'inspect_ai_providers', 'inspect_ai_chat_readiness', 'inspect_ai_upstream_logs', 'inspect_ai_tool_catalog', 'inspect_mcp_setup', 'inspect_mcp_runtime_failures', 'inspect_mcp_authoring_guide', 'inspect_mcp_draft', 'inspect_mcp_tool_schema', 'inspect_ai_guidance', 'inspect_ai_context', 'inspect_current_connection', 'inspect_connection_capabilities', 'inspect_saved_connections', 'inspect_external_sql_directories', 'inspect_external_sql_file', 'inspect_recent_sql_activity', 'inspect_sql_editor_transaction', 'inspect_sql_risk', 'inspect_recent_connection_failures', 'inspect_app_logs', 'inspect_ai_last_render_error', 'inspect_ai_message_flow', 'inspect_ai_context_budget', 'inspect_saved_queries', 'inspect_ai_sessions', 'inspect_sql_snippets', 'inspect_shortcuts', 'get_columns'],
availableToolNames: ['inspect_workspace_tabs', 'inspect_app_health', 'inspect_ai_support_bundle', 'inspect_ai_setup_health', 'inspect_ai_runtime', 'inspect_ai_safety', 'inspect_ai_providers', 'inspect_ai_chat_readiness', 'inspect_ai_upstream_logs', 'inspect_ai_tool_catalog', 'inspect_mcp_setup', 'inspect_mcp_runtime_failures', 'inspect_mcp_authoring_guide', 'inspect_mcp_draft', 'inspect_mcp_tool_schema', 'inspect_ai_guidance', 'inspect_ai_context', 'inspect_current_connection', 'inspect_connection_capabilities', 'inspect_saved_connections', 'inspect_external_sql_directories', 'inspect_external_sql_file', 'inspect_recent_sql_activity', 'inspect_sql_editor_transaction', 'inspect_sql_risk', 'inspect_recent_connection_failures', 'inspect_app_logs', 'inspect_ai_last_render_error', 'inspect_ai_message_flow', 'inspect_ai_context_budget', 'inspect_codebase_hotspots', 'inspect_saved_queries', 'inspect_ai_sessions', 'inspect_sql_snippets', 'inspect_shortcuts', 'get_columns'],
skills,
userPromptSettings,
});
@@ -104,6 +104,7 @@ describe('buildAISystemContextMessages', () => {
expect(joined).toContain('inspect_ai_last_render_error 读取最近一次被隔离的前端渲染异常记录');
expect(joined).toContain('inspect_ai_message_flow 读取当前会话的真实消息结构');
expect(joined).toContain('inspect_ai_context_budget 读取消息、DDL、MCP schema、提示词和 Skills 的体量风险');
expect(joined).toContain('inspect_codebase_hotspots 读取大文件热点');
expect(joined).toContain('inspect_saved_queries');
expect(joined).toContain('inspect_ai_sessions');
expect(joined).toContain('inspect_sql_snippets');

View File

@@ -170,6 +170,12 @@ export const appendDatabaseInspectionGuidanceMessages = (
'inspect_ai_context_budget',
'如果用户提到“AI 变慢”“上下文太大”“表结构挂太多”“工具结果太长”“模型开始乱答”或复杂任务前需要判断是否该拆小上下文,优先调用 inspect_ai_context_budget 读取消息、DDL、MCP schema、提示词和 Skills 的体量风险,再决定收窄上下文或拆任务。',
);
appendGuidanceIfToolAvailable(
messages,
availableToolNames,
'inspect_codebase_hotspots',
'如果用户提到“几千行文件太臃肿”“继续拆分大组件”“下一步该拆哪个文件”“AI/MCP/UI 改动风险大不大”,优先调用 inspect_codebase_hotspots 读取大文件热点、建议拆分切片和测试目标,再制定改动范围。',
);
appendGuidanceIfToolAvailable(
messages,
availableToolNames,

View File

@@ -62,6 +62,7 @@ const TOOL_ACTION_LABELS: Record<string, string> = {
inspect_ai_last_render_error: '读取最近一次 AI 渲染异常',
inspect_ai_message_flow: '诊断当前 AI 消息流',
inspect_ai_context_budget: '诊断 AI 上下文体量风险',
inspect_codebase_hotspots: '读取代码大文件热点',
inspect_saved_queries: '检索本地已保存查询',
inspect_sql_snippets: '读取 SQL 片段模板',
inspect_shortcuts: '读取当前快捷键配置',

View File

@@ -163,6 +163,31 @@ export const BUILTIN_AI_INSPECTION_DIAGNOSTICS_TOOL_INFO: AIBuiltinToolInfo[] =
},
},
},
{
name: "inspect_codebase_hotspots",
icon: "🧱",
desc: "查看前端大文件和拆分热点",
detail:
"返回当前 GoNavi 前端代码中的大文件热点、行数、风险等级、建议拆分切片和应该运行的回归测试。适合用户要求继续治理几千行大文件、评估下一步该拆哪个组件,或 AI 在修改前需要先判断改动风险时调用。",
params: "keyword?, minLines?(默认 1000), limit?(默认 8), includeRecommendations?(默认 true)",
tool: {
type: "function",
function: {
name: "inspect_codebase_hotspots",
description:
"读取 GoNavi 前端大文件和拆分热点快照,返回文件路径、行数、风险等级、建议拆分切片和测试目标。适用于用户提到几千行文件太臃肿、需要继续拆分组件、评估下一个重构切入点或在改 UI/AI/MCP 前需要先判断代码热点风险时优先调用。",
parameters: {
type: "object",
properties: {
keyword: { type: "string", description: "可选,按路径、模块、风险、拆分切片或测试目标过滤,例如 Sidebar、DataGrid、Redis、事务、连接" },
minLines: { type: "number", description: "可选,只返回不少于多少行的热点文件,默认 1000最大 20000" },
limit: { type: "number", description: "可选,最多返回多少个热点,默认 8最大 30" },
includeRecommendations: { type: "boolean", description: "可选,是否返回 suggestedSlices、testTargets 和 nextActions默认 true" },
},
},
},
},
},
{
name: "inspect_sql_snippets",
icon: "🧩",

View File

@@ -89,5 +89,9 @@ describe('describeBuiltinToolParameters', () => {
const mcpFlows = filterBuiltinToolFlows(BUILTIN_TOOL_FLOWS, '运行期失败日志')
.map((flow) => flow.title);
expect(mcpFlows).toContain('排查 MCP 接入状态');
const codebaseFlows = filterBuiltinToolFlows(BUILTIN_TOOL_FLOWS, '拆分热点')
.map((flow) => flow.title);
expect(codebaseFlows).toContain('治理前端大文件');
});
});

View File

@@ -207,6 +207,11 @@ export const BUILTIN_TOOL_FLOWS: AIBuiltinToolFlow[] = [
steps: 'inspect_ai_context_budget -> inspect_ai_context / inspect_ai_message_flow / inspect_ai_tool_catalog',
description: '适合用户反馈 AI 变慢、乱答、上下文太大、工具结果过长或表结构挂太多时先看消息、DDL、MCP schema、提示词和 Skills 的体量来源,再决定收窄上下文或拆任务。',
},
{
title: '治理前端大文件',
steps: 'inspect_codebase_hotspots -> inspect_ai_tool_catalog',
description: '适合用户要求继续拆分几千行组件、评估下一步重构切入点,或 AI 修改 UI/AI/MCP 前先判断大文件拆分热点、风险和验证范围。',
},
{
title: '复用历史 SQL',
steps: 'inspect_saved_queries -> get_columns / execute_sql',

View File

@@ -204,6 +204,14 @@ describe('aiToolRegistry', () => {
expect(info?.tool.function.parameters?.properties?.messageLimit?.description).toContain('最大 120');
});
it('registers the codebase-hotspots inspector as a builtin tool', () => {
const info = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_codebase_hotspots');
expect(info).toBeTruthy();
expect(info?.desc).toContain('前端大文件');
expect(info?.tool.function.description).toContain('拆分热点快照');
expect(info?.tool.function.parameters?.properties?.minLines?.description).toContain('默认 1000');
});
it('registers the recent-sql-activity, saved-query, and sql-snippet inspectors as builtin tools', () => {
const recentActivityTool = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_recent_sql_activity');
const sqlEditorTransactionTool = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_sql_editor_transaction');
@@ -282,6 +290,7 @@ describe('aiToolRegistry', () => {
expect(tools.some((item) => item.function.name === 'inspect_ai_last_render_error')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_message_flow')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_context_budget')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_codebase_hotspots')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_saved_queries')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_sessions')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_sql_snippets')).toBe(true);