@@ -775,6 +775,107 @@ export const CompactThreadButton = {
775775 }
776776}
777777
778+ export const ToolCall = {
779+ template : `
780+ <div v-if="collapsed" @click="collapsed = !collapsed" class="cursor-pointer rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 overflow-hidden">
781+ <!-- Tool Call Header -->
782+ <div class="px-3 py-2 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between bg-gray-50/30 dark:bg-gray-800 space-x-4">
783+ <div class="flex items-center gap-2">
784+ <svg class="size-3.5 text-gray-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>
785+ <span class="font-mono text-xs font-bold text-gray-700 dark:text-gray-300">{{ tool.function.name }}</span>
786+ <span v-if="toolSummary" :title="toolSummary" class="font-mono text-xs text-gray-700 dark:text-gray-300 truncate overflow-hidden xl:max-w-2xl lg:max-w-xl md:max-w-lg sm:max-w-sm max-w-xs">{{ toolSummary }}</span>
787+ </div>
788+ <span class="text-[10px] uppercase tracking-wider text-gray-400 font-medium whitespace-nowrap">Tool Call</span>
789+ </div>
790+ </div>
791+ <div v-else class="rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 overflow-hidden">
792+ <!-- Tool Call Header -->
793+ <div @click="collapsed = !collapsed" class="cursor-pointer px-3 py-2 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between bg-gray-50/30 dark:bg-gray-800 space-x-4">
794+ <div class="flex items-center gap-2">
795+ <svg class="size-3.5 text-gray-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>
796+ <span class="font-mono text-xs font-bold text-gray-700 dark:text-gray-300">{{ tool.function.name }}</span>
797+ </div>
798+ <span class="text-[10px] uppercase tracking-wider text-gray-400 font-medium whitespace-nowrap">Tool Call</span>
799+ </div>
800+
801+ <ToolArguments :value="tool.function.arguments" />
802+
803+ <ToolOutput :tool="tool" :output="toolOutput" />
804+ </div>
805+ ` ,
806+ props : {
807+ thread : {
808+ type : Object ,
809+ required : true
810+ } ,
811+ tool : {
812+ type : Object ,
813+ required : true
814+ }
815+ } ,
816+ setup ( props ) {
817+ const ctx = inject ( 'ctx' )
818+
819+ const collapsed = ref ( true )
820+ const toolOutput = computed ( ( ) => props . thread ?. messages ?. find ( m => m . role === 'tool' && m . tool_call_id === props . tool . id ) )
821+ const toolFailed = computed ( ( ) => {
822+ const output = toolOutput . value
823+ return output ?. content ?. includes ( 'Error' )
824+ } )
825+ const toolArgs = computed ( ( ) => ctx . utils . toJsonObject ( props . tool . function . arguments ) )
826+ const toolSummary = computed ( ( ) => {
827+ const toolName = props . tool . function . name
828+ const args = toolArgs . value
829+ const output = toolOutput . value
830+ if ( toolName == 'run_bash' && args . command ) {
831+ return args . command
832+ }
833+ else if ( toolName == 'skill' && args . name ) {
834+ return args . name
835+ }
836+ else if ( args . path ) {
837+ if ( toolName == 'read_text_file' ) {
838+ return args . path + ' (' + ctx . fmt . humanifyNumber ( output ?. content ?. length || 0 ) + ')'
839+ } else if ( toolName == 'directory_tree' ) {
840+ const tree = ctx . utils . toJsonObject ( output ?. content )
841+ let dirCount = 0
842+ let fileCount = 0
843+ const countItems = ( items ) => {
844+ if ( ! items ) return
845+ items . forEach ( item => {
846+ if ( item . type == 'file' ) {
847+ fileCount ++
848+ } else if ( item . type == 'directory' ) {
849+ dirCount ++
850+ countItems ( item . children )
851+ }
852+ } )
853+ }
854+ countItems ( tree )
855+ return `${ args . path } 📁${ dirCount } 📄${ fileCount } `
856+ }
857+ return args . path
858+ } else if ( toolName == 'open' && args . target ) {
859+ return args . target
860+ } else if ( toolName == 'computer' ) {
861+ if ( args . action ) {
862+ return args . action
863+ }
864+ } else if ( toolName . startsWith ( 'run_' ) && args . code ) {
865+ const firstLine = args . code . split ( '\n' ) [ 0 ]
866+ return firstLine
867+ }
868+ return ''
869+ } )
870+
871+ return {
872+ collapsed,
873+ toolSummary,
874+ toolOutput,
875+ toolFailed,
876+ }
877+ }
878+ }
778879export const ChatBody = {
779880 template : `
780881 <div class="flex flex-col h-full">
@@ -837,7 +938,12 @@ export const ChatBody = {
837938 </div>
838939
839940 <!-- Message bubble -->
840- <div
941+ <div v-if="message.role === 'assistant' && !message.content?.trim() && message.tool_calls && message.tool_calls.length > 0">
942+ <div v-if="message.tool_calls && message.tool_calls.length > 0" class="mb-3 space-y-4">
943+ <ToolCall v-for="(tool, i) in message.tool_calls" :key="i" :thread="currentThread" :tool="tool" />
944+ </div>
945+ </div>
946+ <div v-else
841947 class="message rounded-lg px-4 py-3 relative group"
842948 :class="message.role === 'user'
843949 ? 'bg-blue-100 dark:bg-blue-900 text-gray-900 dark:text-gray-100 border border-blue-200 dark:border-blue-700'
@@ -870,21 +976,7 @@ export const ChatBody = {
870976
871977 <!-- Tool Calls & Outputs -->
872978 <div v-if="message.tool_calls && message.tool_calls.length > 0" class="mb-3 space-y-4">
873- <div v-for="(tool, i) in message.tool_calls" :key="i" class="rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 overflow-hidden">
874- <!-- Tool Call Header -->
875- <div class="px-3 py-2 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between bg-gray-50/30 dark:bg-gray-800 space-x-4">
876- <div class="flex items-center gap-2">
877- <svg class="size-3.5 text-gray-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>
878- <span class="font-mono text-xs font-bold text-gray-700 dark:text-gray-300">{{ tool.function.name }}</span>
879- </div>
880- <span class="text-[10px] uppercase tracking-wider text-gray-400 font-medium">Tool Call</span>
881- </div>
882-
883- <ToolArguments :value="tool.function.arguments" />
884-
885- <ToolOutput :tool="tool" :output="getToolOutput(tool.id)" />
886-
887- </div>
979+ <ToolCall v-for="(tool, i) in message.tool_calls" :key="i" :thread="currentThread" :tool="tool" />
888980 </div>
889981
890982 <!-- Tool Output (Orphaned) -->
0 commit comments