import type { ToolInvocationUIPart } from '@ai-sdk/ui-utils'; import { AnimatePresence, motion } from 'framer-motion'; import { memo, useMemo, useState } from 'react'; import { createHighlighter, type BundledLanguage, type BundledTheme, type HighlighterGeneric } from 'shiki'; import { classNames } from '~/utils/classNames'; import { TOOL_EXECUTION_APPROVAL, TOOL_EXECUTION_DENIED, TOOL_EXECUTION_ERROR, TOOL_NO_EXECUTE_FUNCTION, } from '~/utils/constants'; import { cubicEasingFn } from '~/utils/easings'; import { logger } from '~/utils/logger'; import { themeStore, type Theme } from '~/lib/stores/theme'; import { useStore } from '@nanostores/react'; import type { ToolCallAnnotation } from '~/types/context'; const highlighterOptions = { langs: ['json'], themes: ['light-plus', 'dark-plus'], }; const jsonHighlighter: HighlighterGeneric = import.meta.hot?.data.jsonHighlighter ?? (await createHighlighter(highlighterOptions)); if (import.meta.hot) { import.meta.hot.data.jsonHighlighter = jsonHighlighter; } interface JsonCodeBlockProps { className?: string; code: string; theme: Theme; } function JsonCodeBlock({ className, code, theme }: JsonCodeBlockProps) { let formattedCode = code; try { if (typeof formattedCode === 'object') { formattedCode = JSON.stringify(formattedCode, null, 2); } else if (typeof formattedCode === 'string') { // Attempt to parse and re-stringify for formatting try { const parsed = JSON.parse(formattedCode); formattedCode = JSON.stringify(parsed, null, 2); } catch { // Leave as is if not JSON } } } catch (e) { // If parsing fails, keep original code logger.error('Failed to parse JSON', { error: e }); } return (
); } interface ToolInvocationsProps { toolInvocations: ToolInvocationUIPart[]; toolCallAnnotations: ToolCallAnnotation[]; addToolResult: ({ toolCallId, result }: { toolCallId: string; result: any }) => void; } export const ToolInvocations = memo(({ toolInvocations, toolCallAnnotations, addToolResult }: ToolInvocationsProps) => { const theme = useStore(themeStore); const [showDetails, setShowDetails] = useState(false); const toggleDetails = () => { setShowDetails((prev) => !prev); }; const toolCalls = useMemo( () => toolInvocations.filter((inv) => inv.toolInvocation.state === 'call'), [toolInvocations], ); const toolResults = useMemo( () => toolInvocations.filter((inv) => inv.toolInvocation.state === 'result'), [toolInvocations], ); const hasToolCalls = toolCalls.length > 0; const hasToolResults = toolResults.length > 0; if (!hasToolCalls && !hasToolResults) { return null; } return (
{hasToolResults && (
)}
{hasToolCalls && (
)} {hasToolResults && showDetails && (
)}
); }); const toolVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0 }, }; interface ToolResultsListProps { toolInvocations: ToolInvocationUIPart[]; toolCallAnnotations: ToolCallAnnotation[]; theme: Theme; } const ToolResultsList = memo(({ toolInvocations, toolCallAnnotations, theme }: ToolResultsListProps) => { return (
    {toolInvocations.map((tool, index) => { const toolCallState = tool.toolInvocation.state; if (toolCallState !== 'result') { return null; } const { toolName, toolCallId } = tool.toolInvocation; const annotation = toolCallAnnotations.find((annotation) => { return annotation.toolCallId === toolCallId; }); const isErrorResult = [TOOL_NO_EXECUTE_FUNCTION, TOOL_EXECUTION_DENIED, TOOL_EXECUTION_ERROR].includes( tool.toolInvocation.result, ); return (
    {isErrorResult ? (
    ) : (
    )}
    Server:
    {annotation?.serverName}
    Tool: {toolName}
    Description:{' '} {annotation?.toolDescription}
    Parameters:
    Result:
    ); })}
); }); interface ToolCallsListProps { toolInvocations: ToolInvocationUIPart[]; toolCallAnnotations: ToolCallAnnotation[]; addToolResult: ({ toolCallId, result }: { toolCallId: string; result: any }) => void; theme: Theme; } const ToolCallsList = memo(({ toolInvocations, toolCallAnnotations, addToolResult, theme }: ToolCallsListProps) => { return (
    {toolInvocations.map((tool, index) => { const toolCallState = tool.toolInvocation.state; if (toolCallState !== 'call') { return null; } const { toolName, toolCallId } = tool.toolInvocation; const annotation = toolCallAnnotations.find((annotation) => { return annotation.toolCallId === toolCallId; }); return (
    Bolt wants to use a tool.
    Server:{' '} {annotation?.serverName}
    Tool: {toolName}
    Description:{' '} {annotation?.toolDescription}
    Parameters:
    {' '}
    ); })}
); });