| import type { ElementType } from 'react'; |
| import { |
| FileText, |
| Terminal, |
| ExternalLink, |
| FileEdit, |
| Search, |
| Globe, |
| Code, |
| MessageSquare, |
| Folder, |
| FileX, |
| CloudUpload, |
| Wrench, |
| Cog, |
| Network, |
| FileSearch, |
| FilePlus, |
| PlugIcon, |
| BookOpen, |
| MessageCircleQuestion, |
| CheckCircle2, |
| } from 'lucide-react'; |
|
|
| |
| export const SHOULD_RENDER_TOOL_RESULTS = false; |
|
|
| |
| export function safeJsonParse<T>( |
| jsonString: string | undefined | null, |
| fallback: T, |
| ): T { |
| if (!jsonString) { |
| return fallback; |
| } |
| |
| try { |
| |
| const parsed = JSON.parse(jsonString); |
| |
| |
| if (typeof parsed === 'string' && |
| (parsed.startsWith('{') || parsed.startsWith('['))) { |
| try { |
| |
| return JSON.parse(parsed) as T; |
| } catch (innerError) { |
| |
| return parsed as unknown as T; |
| } |
| } |
| |
| return parsed as T; |
| } catch (outerError) { |
| |
| if (typeof jsonString === 'object') { |
| return jsonString as T; |
| } |
| |
| |
| if (typeof jsonString === 'string') { |
| |
| if (jsonString === 'true') return true as unknown as T; |
| if (jsonString === 'false') return false as unknown as T; |
| if (jsonString === 'null') return null as unknown as T; |
| if (!isNaN(Number(jsonString))) return Number(jsonString) as unknown as T; |
| |
| |
| if (!jsonString.startsWith('{') && !jsonString.startsWith('[')) { |
| return jsonString as unknown as T; |
| } |
| } |
| |
| |
| return fallback; |
| } |
| } |
|
|
| |
| export const getToolIcon = (toolName: string): ElementType => { |
| switch (toolName?.toLowerCase()) { |
| case 'web-browser-takeover': |
| case 'browser-navigate-to': |
| case 'browser-click-element': |
| case 'browser-input-text': |
| case 'browser-scroll-down': |
| case 'browser-scroll-up': |
| case 'browser-click-coordinates': |
| case 'browser-send-keys': |
| case 'browser-switch-tab': |
| case 'browser-go-back': |
| case 'browser-close-tab': |
| case 'browser-drag-drop': |
| case 'browser-get-dropdown-options': |
| case 'browser-select-dropdown-option': |
| case 'browser-scroll-to-text': |
| case 'browser-wait': |
| return Globe; |
|
|
| |
| case 'create-file': |
| return FileEdit; |
| case 'str-replace': |
| return FileSearch; |
| case 'full-file-rewrite': |
| return FilePlus; |
| case 'read-file': |
| return FileText; |
|
|
| |
| case 'execute-command': |
| return Terminal; |
| case 'check-command-output': |
| return Terminal; |
| case 'terminate-command': |
| return Terminal; |
|
|
| |
| case 'web-search': |
| return Search; |
| case 'crawl-webpage': |
| return Globe; |
| case 'scrape-webpage': |
| return Globe; |
|
|
| |
| case 'call-data-provider': |
| return ExternalLink; |
| case 'get-data-provider-endpoints': |
| return Network; |
| case 'execute-data-provider-call': |
| return Network; |
|
|
| |
| case 'delete-file': |
| return FileX; |
|
|
| |
| case 'deploy-site': |
| return CloudUpload; |
|
|
| |
| case 'execute-code': |
| return Code; |
|
|
| |
| case 'ask': |
| return MessageCircleQuestion; |
|
|
| |
| case 'complete': |
| return CheckCircle2; |
|
|
| |
| case 'call-mcp-tool': |
| return PlugIcon; |
|
|
| |
| default: |
| if (toolName?.startsWith('mcp_')) { |
| const parts = toolName.split('_'); |
| if (parts.length >= 3) { |
| const serverName = parts[1]; |
| const toolNamePart = parts.slice(2).join('_'); |
| |
| |
| if (toolNamePart.includes('search') || toolNamePart.includes('web')) { |
| return Search; |
| } else if (toolNamePart.includes('research') || toolNamePart.includes('paper')) { |
| return BookOpen; |
| } else if (serverName === 'exa') { |
| return Search; |
| } |
| } |
| return PlugIcon; |
| } |
| |
| |
| console.log( |
| `[PAGE] Using default icon for unknown tool type: ${toolName}`, |
| ); |
| return Wrench; |
| } |
| }; |
|
|
| |
| export const extractPrimaryParam = ( |
| toolName: string, |
| content: string | undefined, |
| ): string | null => { |
| if (!content) return null; |
|
|
| try { |
| |
| if (toolName?.toLowerCase().startsWith('browser-')) { |
| |
| const urlMatch = content.match(/url=(?:"|')([^"|']+)(?:"|')/); |
| if (urlMatch) return urlMatch[1]; |
|
|
| |
| const goalMatch = content.match(/goal=(?:"|')([^"|']+)(?:"|')/); |
| if (goalMatch) { |
| const goal = goalMatch[1]; |
| return goal.length > 30 ? goal.substring(0, 27) + '...' : goal; |
| } |
|
|
| return null; |
| } |
|
|
| |
| if (content.startsWith('<') && content.includes('>')) { |
| const xmlAttrs = content.match(/<[^>]+\s+([^>]+)>/); |
| if (xmlAttrs && xmlAttrs[1]) { |
| const attrs = xmlAttrs[1].trim(); |
| const filePathMatch = attrs.match(/file_path=["']([^"']+)["']/); |
| if (filePathMatch) { |
| return filePathMatch[1].split('/').pop() || filePathMatch[1]; |
| } |
|
|
| |
| if (toolName?.toLowerCase() === 'execute-command') { |
| const commandMatch = attrs.match(/(?:command|cmd)=["']([^"']+)["']/); |
| if (commandMatch) { |
| const cmd = commandMatch[1]; |
| return cmd.length > 30 ? cmd.substring(0, 27) + '...' : cmd; |
| } |
| } |
| } |
| } |
|
|
| |
| let match: RegExpMatchArray | null = null; |
|
|
| switch (toolName?.toLowerCase()) { |
| |
| case 'create-file': |
| case 'full-file-rewrite': |
| case 'read-file': |
| case 'delete-file': |
| case 'str-replace': |
| |
| match = content.match(/file_path=(?:"|')([^"|']+)(?:"|')/); |
| |
| return match ? match[1].split('/').pop() || match[1] : null; |
|
|
| |
| case 'execute-command': |
| |
| match = content.match(/command=(?:"|')([^"|']+)(?:"|')/); |
| if (match) { |
| const cmd = match[1]; |
| return cmd.length > 30 ? cmd.substring(0, 27) + '...' : cmd; |
| } |
| return null; |
|
|
| |
| case 'web-search': |
| match = content.match(/query=(?:"|')([^"|']+)(?:"|')/); |
| return match |
| ? match[1].length > 30 |
| ? match[1].substring(0, 27) + '...' |
| : match[1] |
| : null; |
|
|
| |
| case 'call-data-provider': |
| match = content.match(/service_name=(?:"|')([^"|']+)(?:"|')/); |
| const route = content.match(/route=(?:"|')([^"|']+)(?:"|')/); |
| return match && route |
| ? `${match[1]}/${route[1]}` |
| : match |
| ? match[1] |
| : null; |
|
|
| |
| case 'deploy-site': |
| match = content.match(/site_name=(?:"|')([^"|']+)(?:"|')/); |
| return match ? match[1] : null; |
| } |
|
|
| return null; |
| } catch (e) { |
| console.warn('Error parsing tool parameters:', e); |
| return null; |
| } |
| }; |
|
|
| const TOOL_DISPLAY_NAMES = new Map([ |
| ['execute-command', 'Executing Command'], |
| ['check-command-output', 'Checking Command Output'], |
| ['terminate-command', 'Terminating Command'], |
| ['list-commands', 'Listing Commands'], |
| |
| ['create-file', 'Creating File'], |
| ['delete-file', 'Deleting File'], |
| ['full-file-rewrite', 'Rewriting File'], |
| ['str-replace', 'Editing Text'], |
| ['str_replace', 'Editing Text'], |
| |
| ['browser-click-element', 'Clicking Element'], |
| ['browser-close-tab', 'Closing Tab'], |
| ['browser-drag-drop', 'Dragging Element'], |
| ['browser-get-dropdown-options', 'Getting Options'], |
| ['browser-go-back', 'Going Back'], |
| ['browser-input-text', 'Entering Text'], |
| ['browser-navigate-to', 'Navigating to Page'], |
| ['browser-scroll-down', 'Scrolling Down'], |
| ['browser-scroll-to-text', 'Scrolling to Text'], |
| ['browser-scroll-up', 'Scrolling Up'], |
| ['browser-select-dropdown-option', 'Selecting Option'], |
| ['browser-click-coordinates', 'Clicking Coordinates'], |
| ['browser-send-keys', 'Pressing Keys'], |
| ['browser-switch-tab', 'Switching Tab'], |
| ['browser-wait', 'Waiting'], |
|
|
| ['execute-data-provider-call', 'Calling data provider'], |
| ['execute_data_provider_call', 'Calling data provider'], |
| ['get-data-provider-endpoints', 'Getting endpoints'], |
| |
| ['deploy', 'Deploying'], |
| ['ask', 'Ask'], |
| ['complete', 'Completing Task'], |
| ['crawl-webpage', 'Crawling Website'], |
| ['expose-port', 'Exposing Port'], |
| ['scrape-webpage', 'Scraping Website'], |
| ['web-search', 'Searching Web'], |
| ['see-image', 'Viewing Image'], |
| |
| ['call-mcp-tool', 'External Tool'], |
|
|
| ['update-agent', 'Updating Agent'], |
| ['get-current-agent-config', 'Getting Agent Config'], |
| ['search-mcp-servers', 'Searching MCP Servers'], |
| ['get-mcp-server-tools', 'Getting MCP Server Tools'], |
| ['configure-mcp-server', 'Configuring MCP Server'], |
| ['get-popular-mcp-servers', 'Getting Popular MCP Servers'], |
| ['test-mcp-server-connection', 'Testing MCP Server Connection'], |
|
|
|
|
| |
|
|
| ['execute_command', 'Executing Command'], |
| ['check_command_output', 'Checking Command Output'], |
| ['terminate_command', 'Terminating Command'], |
| ['list_commands', 'Listing Commands'], |
| |
| ['create_file', 'Creating File'], |
| ['delete_file', 'Deleting File'], |
| ['full_file_rewrite', 'Rewriting File'], |
| ['str_replace', 'Editing Text'], |
| |
| ['browser_click_element', 'Clicking Element'], |
| ['browser_close_tab', 'Closing Tab'], |
| ['browser_drag_drop', 'Dragging Element'], |
| ['browser_get_dropdown_options', 'Getting Options'], |
| ['browser_go_back', 'Going Back'], |
| ['browser_input_text', 'Entering Text'], |
| ['browser_navigate_to', 'Navigating to Page'], |
| ['browser_scroll_down', 'Scrolling Down'], |
| ['browser_scroll_to_text', 'Scrolling to Text'], |
| ['browser_scroll_up', 'Scrolling Up'], |
| ['browser_select_dropdown_option', 'Selecting Option'], |
| ['browser_click_coordinates', 'Clicking Coordinates'], |
| ['browser_send_keys', 'Pressing Keys'], |
| ['browser_switch_tab', 'Switching Tab'], |
| ['browser_wait', 'Waiting'], |
|
|
| ['execute_data_provider_call', 'Calling data provider'], |
| ['get_data_provider_endpoints', 'Getting endpoints'], |
| |
| ['deploy', 'Deploying'], |
| ['ask', 'Ask'], |
| ['complete', 'Completing Task'], |
| ['crawl_webpage', 'Crawling Website'], |
| ['expose_port', 'Exposing Port'], |
| ['scrape_webpage', 'Scraping Website'], |
| ['web_search', 'Searching Web'], |
| ['see_image', 'Viewing Image'], |
| |
| ['call_mcp_tool', 'External Tool'], |
|
|
| ['update_agent', 'Updating Agent'], |
| ['get_current_agent_config', 'Getting Agent Config'], |
| ['search_mcp_servers', 'Searching MCP Servers'], |
| ['get_mcp_server_tools', 'Getting MCP Server Tools'], |
| ['configure_mcp_server', 'Configuring MCP Server'], |
| ['get_popular_mcp_servers', 'Getting Popular MCP Servers'], |
| ['test_mcp_server_connection', 'Testing MCP Server Connection'], |
|
|
| ]); |
|
|
|
|
| const MCP_SERVER_NAMES = new Map([ |
| ['exa', 'Exa Search'], |
| ['github', 'GitHub'], |
| ['notion', 'Notion'], |
| ['slack', 'Slack'], |
| ['filesystem', 'File System'], |
| ['memory', 'Memory'], |
| ]); |
|
|
| function formatMCPToolName(serverName: string, toolName: string): string { |
| const serverMappings: Record<string, string> = { |
| 'exa': 'Exa Search', |
| 'github': 'GitHub', |
| 'notion': 'Notion', |
| 'slack': 'Slack', |
| 'filesystem': 'File System', |
| 'memory': 'Memory', |
| 'anthropic': 'Anthropic', |
| 'openai': 'OpenAI', |
| 'composio': 'Composio', |
| 'langchain': 'LangChain', |
| 'llamaindex': 'LlamaIndex' |
| }; |
| |
| const formattedServerName = serverMappings[serverName.toLowerCase()] || |
| serverName.charAt(0).toUpperCase() + serverName.slice(1); |
| |
| let formattedToolName = toolName; |
| |
| if (toolName.includes('-')) { |
| formattedToolName = toolName |
| .split('-') |
| .map(word => word.charAt(0).toUpperCase() + word.slice(1)) |
| .join(' '); |
| } |
| else if (toolName.includes('_')) { |
| formattedToolName = toolName |
| .split('_') |
| .map(word => word.charAt(0).toUpperCase() + word.slice(1)) |
| .join(' '); |
| } |
| else if (/[a-z][A-Z]/.test(toolName)) { |
| formattedToolName = toolName |
| .replace(/([a-z])([A-Z])/g, '$1 $2') |
| .split(' ') |
| .map(word => word.charAt(0).toUpperCase() + word.slice(1)) |
| .join(' '); |
| } |
| else { |
| formattedToolName = toolName.charAt(0).toUpperCase() + toolName.slice(1); |
| } |
| |
| return `${formattedServerName}: ${formattedToolName}`; |
| } |
|
|
| export function getUserFriendlyToolName(toolName: string): string { |
| if (toolName.startsWith('mcp_')) { |
| const parts = toolName.split('_'); |
| if (parts.length >= 3) { |
| const serverName = parts[1]; |
| const toolNamePart = parts.slice(2).join('_'); |
| return formatMCPToolName(serverName, toolNamePart); |
| } |
| } |
| if (toolName.includes('-') && !TOOL_DISPLAY_NAMES.has(toolName)) { |
| const parts = toolName.split('-'); |
| if (parts.length >= 2) { |
| const serverName = parts[0]; |
| const toolNamePart = parts.slice(1).join('-'); |
| return formatMCPToolName(serverName, toolNamePart); |
| } |
| } |
| return TOOL_DISPLAY_NAMES.get(toolName) || toolName; |
| } |
|
|