import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'; import * as React from 'react'; import { SubAgentProvider } from 'src/components/CtrlOToExpand.js'; import { FallbackToolUseErrorMessage } from 'src/components/FallbackToolUseErrorMessage.js'; import { FallbackToolUseRejectedMessage } from 'src/components/FallbackToolUseRejectedMessage.js'; import type { z } from 'zod/v4'; import type { Command } from 'src/commands.js'; import { Byline } from '@anthropic/ink'; import { Message as MessageComponent } from 'src/components/Message.js'; import { MessageResponse } from 'src/components/MessageResponse.js'; import { Box, Text } from '@anthropic/ink'; import type { Tools } from 'src/Tool.js'; import type { ProgressMessage } from 'src/types/message.js'; import { buildSubagentLookups, EMPTY_LOOKUPS } from 'src/utils/messages.js'; import { plural } from 'src/utils/stringUtils.js'; import type { inputSchema, Output, Progress } from './SkillTool.js'; type Input = z.infer>; const MAX_PROGRESS_MESSAGES_TO_SHOW = 3; const INITIALIZING_TEXT = 'Initializing…'; export function renderToolResultMessage(output: Output): React.ReactNode { // Handle forked skill result if ('status' in output && output.status === 'forked') { return ( {['Done']} ); } const parts: string[] = ['Successfully loaded skill']; // Show tools count (only for inline skills) if ('allowedTools' in output && output.allowedTools && output.allowedTools.length > 0) { const count = output.allowedTools.length; parts.push(`${count} ${plural(count, 'tool')} allowed`); } // Show model if non-default (only for inline skills) if ('model' in output && output.model) { parts.push(output.model); } return ( {parts} ); } export function renderToolUseMessage( { skill }: Partial, { commands }: { commands?: Command[] }, ): React.ReactNode { if (!skill) { return null; } // Look up the command to check if it came from the legacy /commands folder const command = commands?.find(c => c.name === skill); const displayName = command?.loadedFrom === 'commands_DEPRECATED' ? `/${skill}` : skill; return displayName; } export function renderToolUseProgressMessage( progressMessages: ProgressMessage[], { tools, verbose, }: { tools: Tools; verbose: boolean; }, ): React.ReactNode { if (!progressMessages.length) { return ( {INITIALIZING_TEXT} ); } // Take only the last few messages for display in non-verbose mode const displayedMessages = verbose ? progressMessages : progressMessages.slice(-MAX_PROGRESS_MESSAGES_TO_SHOW); const hiddenCount = progressMessages.length - displayedMessages.length; const { inProgressToolUseIDs } = buildSubagentLookups(progressMessages.map(pm => pm.data)); return ( {displayedMessages.map(progressMessage => ( ))} {hiddenCount > 0 && ( +{hiddenCount} more tool {plural(hiddenCount, 'use')} )} ); } export function renderToolUseRejectedMessage( _input: Input, { progressMessagesForMessage, tools, verbose, }: { progressMessagesForMessage: ProgressMessage[]; tools: Tools; verbose: boolean; }, ): React.ReactNode { return ( <> {renderToolUseProgressMessage(progressMessagesForMessage, { tools, verbose, })} ); } export function renderToolUseErrorMessage( result: ToolResultBlockParam['content'], { progressMessagesForMessage, tools, verbose, }: { progressMessagesForMessage: ProgressMessage[]; tools: Tools; verbose: boolean; }, ): React.ReactNode { return ( <> {renderToolUseProgressMessage(progressMessagesForMessage, { tools, verbose, })} ); }