@ash-cloud/ash-ui 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAyVO,SAAS,mBAAmB,MAAA,EAAgD;AACjF,EAAA,OAAO,OAAO,MAAA,KAAW,aAAA;AAC3B;AAEO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,eAAe,MAAA,EAA4C;AACzE,EAAA,OAAO,OAAO,MAAA,KAAW,QAAA;AAC3B;AAEO,SAAS,aAAa,MAAA,EAA0C;AACrE,EAAA,OAAO,OAAO,MAAA,KAAW,MAAA;AAC3B;AAEO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,gBAAgB,MAAA,EAA6C;AAC3E,EAAA,OAAO,OAAO,MAAA,KAAW,UAAA;AAC3B;AAEO,SAAS,oBAAoB,MAAA,EAAiD;AACnF,EAAA,OAAO,OAAO,MAAA,KAAW,cAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,gBAAgB,KAAA,EAAoD;AAClF,EAAA,OAAO,MAAM,IAAA,KAAS,WAAA;AACxB;AAEO,SAAS,aAAa,KAAA,EAAiD;AAC5E,EAAA,OAAO,MAAM,IAAA,KAAS,OAAA;AACxB;AAEO,SAAS,cAAc,KAAA,EAAkD;AAC9E,EAAA,OAAO,MAAM,IAAA,KAAS,QAAA;AACxB;AAsIO,IAAM,sBAAA,GAA4C;AAAA,EACvD,IAAA,EAAM,QAAA;AAAA,EACN,oBAAA,EAAsB,CAAA;AAAA,EACtB,eAAA,EAAiB,KAAA;AAAA,EACjB,iBAAA,EAAmB;AACrB","file":"types.js","sourcesContent":["/**\n * @ash-cloud/ash-ui - Types\n *\n * Normalized types for structured tool call display in agentic UIs.\n * These types provide a unified representation for displaying tool calls\n * with their arguments and results together.\n */\n\nimport type { ReactNode } from 'react';\n\n// =============================================================================\n// Tool Status\n// =============================================================================\n\n/**\n * Status of a tool call execution\n */\nexport type ToolStatus = 'pending' | 'success' | 'failed';\n\n// =============================================================================\n// Action-Specific Result Types\n// =============================================================================\n\n/**\n * Result from a command/bash execution\n */\nexport interface CommandRunResult {\n exitCode?: number;\n success?: boolean;\n output?: string;\n}\n\n/**\n * Generic tool result that can be markdown or structured JSON\n */\nexport interface ToolResult {\n type: 'markdown' | 'json';\n value: unknown;\n}\n\n// =============================================================================\n// Action Types (Discriminated Union)\n// =============================================================================\n\n/**\n * Bash/command execution action\n */\nexport interface CommandRunAction {\n action: 'command_run';\n command: string;\n description?: string;\n result?: CommandRunResult;\n}\n\n/**\n * File read action\n */\nexport interface FileReadAction {\n action: 'file_read';\n path: string;\n offset?: number;\n limit?: number;\n /** Number of lines read (for display) */\n linesRead?: number;\n}\n\n/**\n * File edit action\n */\nexport interface FileEditAction {\n action: 'file_edit';\n path: string;\n oldString?: string;\n newString?: string;\n replaceAll?: boolean;\n /** Number of lines added */\n linesAdded?: number;\n /** Number of lines removed */\n linesRemoved?: number;\n}\n\n/**\n * File write action\n */\nexport interface FileWriteAction {\n action: 'file_write';\n path: string;\n content?: string;\n /** Number of lines written */\n linesWritten?: number;\n}\n\n/**\n * Search/grep action\n */\nexport interface SearchAction {\n action: 'search';\n pattern: string;\n path?: string;\n glob?: string;\n type?: string;\n}\n\n/**\n * Glob/file pattern matching action\n */\nexport interface GlobAction {\n action: 'glob';\n pattern: string;\n path?: string;\n}\n\n/**\n * Web fetch action\n */\nexport interface WebFetchAction {\n action: 'web_fetch';\n url: string;\n prompt?: string;\n}\n\n/**\n * Web search action\n */\nexport interface WebSearchAction {\n action: 'web_search';\n query: string;\n}\n\n/**\n * MCP (Model Context Protocol) tool action\n */\nexport interface McpToolAction {\n action: 'mcp_tool';\n serverName: string;\n toolName: string;\n arguments?: unknown;\n result?: ToolResult;\n}\n\n/**\n * Generic/unknown tool action\n */\nexport interface GenericToolAction {\n action: 'generic_tool';\n toolName: string;\n arguments?: unknown;\n result?: ToolResult;\n}\n\n// =============================================================================\n// Todo Types\n// =============================================================================\n\n/**\n * Status of a todo item\n */\nexport type TodoStatus = 'pending' | 'in_progress' | 'completed';\n\n/**\n * A single todo item from TodoWrite tool\n */\nexport interface TodoItem {\n /** Task content - imperative form (e.g., \"Run tests\") */\n content: string;\n /** Current status of the todo */\n status: TodoStatus;\n /** Active form shown during execution (e.g., \"Running tests\") */\n activeForm: string;\n}\n\n/**\n * TodoWrite tool action - tracks task progress\n */\nexport interface TodoWriteAction {\n action: 'todo_write';\n /** All todos in the current list */\n todos: TodoItem[];\n /** Summary statistics */\n stats?: {\n total: number;\n completed: number;\n inProgress: number;\n pending: number;\n };\n}\n\n/**\n * Discriminated union of all action types\n */\nexport type ActionType =\n | CommandRunAction\n | FileReadAction\n | FileEditAction\n | FileWriteAction\n | SearchAction\n | GlobAction\n | WebFetchAction\n | WebSearchAction\n | McpToolAction\n | GenericToolAction\n | TodoWriteAction;\n\n// =============================================================================\n// Normalized Tool Call\n// =============================================================================\n\n/**\n * A normalized tool call that combines tool_use and tool_result data\n */\nexport interface NormalizedToolCall {\n /** Unique ID from the tool_use block */\n id: string;\n\n /** Original tool name (e.g., 'Bash', 'Read', 'mcp__server__tool') */\n toolName: string;\n\n /** Parsed action type with structured arguments and result */\n actionType: ActionType;\n\n /** Current execution status */\n status: ToolStatus;\n\n /** Human-readable one-liner summary */\n summary: string;\n\n /** When the tool call started */\n startedAt?: string;\n\n /** When the tool call completed */\n completedAt?: string;\n\n /** Whether this tool call resulted in an error */\n isError?: boolean;\n}\n\n// =============================================================================\n// Normalized Entry Types\n// =============================================================================\n\n/**\n * User message entry\n */\nexport interface UserMessageEntry {\n type: 'user_message';\n}\n\n/**\n * Assistant message entry\n */\nexport interface AssistantMessageEntry {\n type: 'assistant_message';\n}\n\n/**\n * Thinking/reasoning entry (extended thinking)\n */\nexport interface ThinkingEntry {\n type: 'thinking';\n}\n\n/**\n * Tool call entry with full tool call data\n */\nexport interface ToolCallEntry {\n type: 'tool_call';\n toolCall: NormalizedToolCall;\n}\n\n/**\n * Error entry\n */\nexport interface ErrorEntry {\n type: 'error';\n message: string;\n code?: string;\n}\n\n/**\n * Custom widget entry - rendered via consumer-provided render function\n *\n * Use this for interactive UI components like:\n * - Image grid selectors\n * - Product carousels\n * - Form inputs\n * - Data visualizations\n * - Any custom interactive widget\n *\n * @example\n * ```tsx\n * const entry: NormalizedEntry = {\n * id: 'widget-1',\n * entryType: {\n * type: 'widget',\n * widgetType: 'image-grid',\n * widgetData: { images: [...], selected: null },\n * },\n * content: 'Select an image',\n * };\n * ```\n */\nexport interface WidgetEntry {\n type: 'widget';\n /** Widget type identifier (e.g., 'image-grid', 'product-selector') */\n widgetType: string;\n /** Widget-specific data passed to the render function */\n widgetData: unknown;\n}\n\n/**\n * Discriminated union of all entry types\n */\nexport type NormalizedEntryType =\n | UserMessageEntry\n | AssistantMessageEntry\n | ThinkingEntry\n | ToolCallEntry\n | ErrorEntry\n | WidgetEntry;\n\n// =============================================================================\n// Normalized Entry\n// =============================================================================\n\n/**\n * A normalized conversation entry for display\n */\nexport interface NormalizedEntry {\n /** Unique entry ID */\n id: string;\n\n /** ISO timestamp */\n timestamp?: string;\n\n /** Entry type with type-specific data */\n entryType: NormalizedEntryType;\n\n /** Text content for display */\n content: string;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\nexport function isCommandRunAction(action: ActionType): action is CommandRunAction {\n return action.action === 'command_run';\n}\n\nexport function isFileReadAction(action: ActionType): action is FileReadAction {\n return action.action === 'file_read';\n}\n\nexport function isFileEditAction(action: ActionType): action is FileEditAction {\n return action.action === 'file_edit';\n}\n\nexport function isFileWriteAction(action: ActionType): action is FileWriteAction {\n return action.action === 'file_write';\n}\n\nexport function isSearchAction(action: ActionType): action is SearchAction {\n return action.action === 'search';\n}\n\nexport function isGlobAction(action: ActionType): action is GlobAction {\n return action.action === 'glob';\n}\n\nexport function isWebFetchAction(action: ActionType): action is WebFetchAction {\n return action.action === 'web_fetch';\n}\n\nexport function isWebSearchAction(action: ActionType): action is WebSearchAction {\n return action.action === 'web_search';\n}\n\nexport function isMcpToolAction(action: ActionType): action is McpToolAction {\n return action.action === 'mcp_tool';\n}\n\nexport function isGenericToolAction(action: ActionType): action is GenericToolAction {\n return action.action === 'generic_tool';\n}\n\nexport function isTodoWriteAction(action: ActionType): action is TodoWriteAction {\n return action.action === 'todo_write';\n}\n\nexport function isToolCallEntry(entry: NormalizedEntryType): entry is ToolCallEntry {\n return entry.type === 'tool_call';\n}\n\nexport function isErrorEntry(entry: NormalizedEntryType): entry is ErrorEntry {\n return entry.type === 'error';\n}\n\nexport function isWidgetEntry(entry: NormalizedEntryType): entry is WidgetEntry {\n return entry.type === 'widget';\n}\n\n// =============================================================================\n// Widget Rendering\n// =============================================================================\n\n/**\n * Props passed to the widget render function\n */\nexport interface WidgetRenderProps<T = unknown> {\n /** The widget entry being rendered */\n entry: NormalizedEntry;\n /** Widget type identifier */\n widgetType: string;\n /** Widget-specific data */\n widgetData: T;\n /** Callback for widget interactions (e.g., selection, submit) */\n onAction?: (action: WidgetAction) => void;\n}\n\n/**\n * Action dispatched from a widget interaction\n */\nexport interface WidgetAction {\n /** Action type (e.g., 'select', 'submit', 'cancel') */\n type: string;\n /** Widget type that dispatched the action */\n widgetType: string;\n /** Entry ID of the widget */\n entryId: string;\n /** Action-specific payload */\n payload?: unknown;\n}\n\n/**\n * Function type for rendering custom widgets\n *\n * @example\n * ```tsx\n * const renderWidget: WidgetRenderFunction = ({ widgetType, widgetData, onAction }) => {\n * switch (widgetType) {\n * case 'image-grid':\n * return (\n * <ImageGrid\n * images={widgetData.images}\n * onSelect={(id) => onAction?.({ type: 'select', widgetType, entryId, payload: { id } })}\n * />\n * );\n * case 'product-selector':\n * return <ProductSelector data={widgetData} onAction={onAction} />;\n * default:\n * return null;\n * }\n * };\n *\n * <MessageList entries={entries} renderWidget={renderWidget} />\n * ```\n */\nexport type WidgetRenderFunction = (props: WidgetRenderProps) => ReactNode;\n\n// =============================================================================\n// Log Types (for SandboxLogsPanel)\n// =============================================================================\n\nexport type LogLevel = 'info' | 'warn' | 'error' | 'debug';\nexport type LogCategory = 'setup' | 'skills' | 'execution' | 'process' | 'startup';\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n category: LogCategory;\n message: string;\n data?: Record<string, unknown>;\n}\n\n// =============================================================================\n// File Attachment\n// =============================================================================\n\nexport interface FileAttachment {\n name: string;\n type: string;\n size: number;\n base64: string;\n}\n\n// =============================================================================\n// Display Mode Configuration\n// =============================================================================\n\n/**\n * Display mode for tool calls in the chat interface\n *\n * - 'inline': Each tool call rendered inline as expandable cards (current behavior)\n * - 'compact': Single animated status line that updates with each tool call,\n * with accordion to expand and see execution details. Text streams unbroken.\n * - 'accordion': Simpler step-based accordion list that accumulates tool calls\n * with icons, labels, and durations (like Vidra's ThinkingIndicator).\n */\nexport type ToolDisplayMode = 'inline' | 'compact' | 'accordion';\n\n/**\n * Configuration for tool display behavior\n */\nexport interface ToolDisplayConfig {\n /**\n * Display mode for tool calls\n * @default 'inline'\n */\n mode: ToolDisplayMode;\n\n /**\n * For 'compact' mode: break into a new group every N tool calls\n * Set to 0 or undefined to never break (all tool calls in single group)\n * @default 0\n */\n breakEveryNToolCalls?: number;\n\n /**\n * Whether tool call groups start expanded\n * @default false\n */\n defaultExpanded?: boolean;\n\n /**\n * Animation duration for status line transitions (ms)\n * @default 300\n */\n animationDuration?: number;\n}\n\n/**\n * Default display configuration\n */\nexport const DEFAULT_DISPLAY_CONFIG: ToolDisplayConfig = {\n mode: 'inline',\n breakEveryNToolCalls: 0,\n defaultExpanded: false,\n animationDuration: 300,\n};\n\n// =============================================================================\n// Tool Execution Group (for compact mode)\n// =============================================================================\n\n/**\n * A group of consecutive tool calls that are displayed together\n * in compact mode. Text content before/after flows around the group.\n */\nexport interface ToolExecutionGroup {\n /** Unique ID for the group */\n id: string;\n\n /** Tool calls in this group (in order) */\n toolCalls: NormalizedToolCall[];\n\n /** Index of the currently active/latest tool call for status display */\n activeIndex: number;\n\n /** Whether all tool calls in this group have completed */\n isComplete: boolean;\n\n /** Overall status of the group */\n status: 'pending' | 'success' | 'partial_failure' | 'failed';\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AA6XO,SAAS,mBAAmB,MAAA,EAAgD;AACjF,EAAA,OAAO,OAAO,MAAA,KAAW,aAAA;AAC3B;AAEO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,eAAe,MAAA,EAA4C;AACzE,EAAA,OAAO,OAAO,MAAA,KAAW,QAAA;AAC3B;AAEO,SAAS,aAAa,MAAA,EAA0C;AACrE,EAAA,OAAO,OAAO,MAAA,KAAW,MAAA;AAC3B;AAEO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,gBAAgB,MAAA,EAA6C;AAC3E,EAAA,OAAO,OAAO,MAAA,KAAW,UAAA;AAC3B;AAEO,SAAS,oBAAoB,MAAA,EAAiD;AACnF,EAAA,OAAO,OAAO,MAAA,KAAW,cAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,kBAAkB,MAAA,EAA+C;AAC/E,EAAA,OAAO,OAAO,MAAA,KAAW,YAAA;AAC3B;AAEO,SAAS,gBAAgB,KAAA,EAAoD;AAClF,EAAA,OAAO,MAAM,IAAA,KAAS,WAAA;AACxB;AAEO,SAAS,aAAa,KAAA,EAAiD;AAC5E,EAAA,OAAO,MAAM,IAAA,KAAS,OAAA;AACxB;AAEO,SAAS,cAAc,KAAA,EAAkD;AAC9E,EAAA,OAAO,MAAM,IAAA,KAAS,QAAA;AACxB;AAsIO,IAAM,sBAAA,GAA4C;AAAA,EACvD,IAAA,EAAM,QAAA;AAAA,EACN,oBAAA,EAAsB,CAAA;AAAA,EACtB,eAAA,EAAiB,KAAA;AAAA,EACjB,iBAAA,EAAmB;AACrB","file":"types.js","sourcesContent":["/**\n * @ash-cloud/ash-ui - Types\n *\n * Normalized types for structured tool call display in agentic UIs.\n * These types provide a unified representation for displaying tool calls\n * with their arguments and results together.\n */\n\nimport type { ReactNode } from 'react';\n\n// =============================================================================\n// Tool Status\n// =============================================================================\n\n/**\n * Status of a tool call execution\n */\nexport type ToolStatus = 'pending' | 'success' | 'failed';\n\n// =============================================================================\n// Action-Specific Result Types\n// =============================================================================\n\n/**\n * Result from a command/bash execution\n */\nexport interface CommandRunResult {\n exitCode?: number;\n success?: boolean;\n output?: string;\n}\n\n/**\n * Generic tool result that can be markdown or structured JSON\n */\nexport interface ToolResult {\n type: 'markdown' | 'json';\n value: unknown;\n}\n\n// =============================================================================\n// Action Types (Discriminated Union)\n// =============================================================================\n\n/**\n * Bash/command execution action\n */\nexport interface CommandRunAction {\n action: 'command_run';\n command: string;\n description?: string;\n result?: CommandRunResult;\n}\n\n/**\n * File read action\n */\nexport interface FileReadAction {\n action: 'file_read';\n path: string;\n offset?: number;\n limit?: number;\n /** Number of lines read (for display) */\n linesRead?: number;\n}\n\n/**\n * File edit action\n */\nexport interface FileEditAction {\n action: 'file_edit';\n path: string;\n oldString?: string;\n newString?: string;\n replaceAll?: boolean;\n /** Number of lines added */\n linesAdded?: number;\n /** Number of lines removed */\n linesRemoved?: number;\n}\n\n/**\n * File write action\n */\nexport interface FileWriteAction {\n action: 'file_write';\n path: string;\n content?: string;\n /** Number of lines written */\n linesWritten?: number;\n}\n\n/**\n * Search/grep action\n */\nexport interface SearchAction {\n action: 'search';\n pattern: string;\n path?: string;\n glob?: string;\n type?: string;\n}\n\n/**\n * Glob/file pattern matching action\n */\nexport interface GlobAction {\n action: 'glob';\n pattern: string;\n path?: string;\n}\n\n/**\n * Web fetch action\n */\nexport interface WebFetchAction {\n action: 'web_fetch';\n url: string;\n prompt?: string;\n}\n\n/**\n * Web search action\n */\nexport interface WebSearchAction {\n action: 'web_search';\n query: string;\n}\n\n/**\n * MCP (Model Context Protocol) tool action\n */\nexport interface McpToolAction {\n action: 'mcp_tool';\n serverName: string;\n toolName: string;\n arguments?: unknown;\n result?: ToolResult;\n}\n\n/**\n * Generic/unknown tool action\n */\nexport interface GenericToolAction {\n action: 'generic_tool';\n toolName: string;\n arguments?: unknown;\n result?: ToolResult;\n}\n\n/**\n * Agent/Task tool action - represents a sub-agent that can have nested tool calls\n */\nexport interface AgentToolAction {\n action: 'agent_tool';\n /** Agent type (e.g., 'Explore', 'Plan', 'Bash', 'general-purpose') */\n agentType: string;\n /** Description of what the agent is doing */\n description: string;\n /** Full prompt given to the agent */\n prompt?: string;\n /** Nested tool calls made by this agent */\n nestedToolCalls?: NormalizedToolCall[];\n /** Current count of tool calls (updates during streaming) */\n toolCallCount?: number;\n /** When the agent started */\n startedAt?: string;\n /** When the agent completed */\n completedAt?: string;\n /** Result/output from the agent */\n result?: ToolResult;\n}\n\n// =============================================================================\n// Todo Types\n// =============================================================================\n\n/**\n * Status of a todo item\n */\nexport type TodoStatus = 'pending' | 'in_progress' | 'completed';\n\n/**\n * A single todo item from TodoWrite tool\n */\nexport interface TodoItem {\n /** Task content - imperative form (e.g., \"Run tests\") */\n content: string;\n /** Current status of the todo */\n status: TodoStatus;\n /** Active form shown during execution (e.g., \"Running tests\") */\n activeForm: string;\n}\n\n/**\n * TodoWrite tool action - tracks task progress\n */\nexport interface TodoWriteAction {\n action: 'todo_write';\n /** All todos in the current list */\n todos: TodoItem[];\n /** Summary statistics */\n stats?: {\n total: number;\n completed: number;\n inProgress: number;\n pending: number;\n };\n}\n\n/**\n * Discriminated union of all action types\n */\nexport type ActionType =\n | CommandRunAction\n | FileReadAction\n | FileEditAction\n | FileWriteAction\n | SearchAction\n | GlobAction\n | WebFetchAction\n | WebSearchAction\n | McpToolAction\n | GenericToolAction\n | TodoWriteAction\n | AgentToolAction;\n\n// =============================================================================\n// Normalized Tool Call\n// =============================================================================\n\n/**\n * A normalized tool call that combines tool_use and tool_result data\n */\nexport interface NormalizedToolCall {\n /** Unique ID from the tool_use block */\n id: string;\n\n /** Original tool name (e.g., 'Bash', 'Read', 'mcp__server__tool') */\n toolName: string;\n\n /** Parsed action type with structured arguments and result */\n actionType: ActionType;\n\n /** Current execution status */\n status: ToolStatus;\n\n /** Human-readable one-liner summary */\n summary: string;\n\n /** Raw input from tool_use event (for display in UI) */\n input?: unknown;\n\n /** Raw output/result from tool_result event (for display in UI) */\n output?: unknown;\n\n /** When the tool call started */\n startedAt?: string;\n\n /** When the tool call completed */\n completedAt?: string;\n\n /** Whether this tool call resulted in an error */\n isError?: boolean;\n\n /** Nested tool calls (for agent/sub-agent tools like Task) */\n nestedToolCalls?: NormalizedToolCall[];\n\n /** Current count of nested tool calls (updates during streaming) */\n nestedToolCallCount?: number;\n}\n\n// =============================================================================\n// Normalized Entry Types\n// =============================================================================\n\n/**\n * User message entry\n */\nexport interface UserMessageEntry {\n type: 'user_message';\n}\n\n/**\n * Assistant message entry\n */\nexport interface AssistantMessageEntry {\n type: 'assistant_message';\n}\n\n/**\n * Thinking/reasoning entry (extended thinking)\n */\nexport interface ThinkingEntry {\n type: 'thinking';\n}\n\n/**\n * Tool call entry with full tool call data\n */\nexport interface ToolCallEntry {\n type: 'tool_call';\n toolCall: NormalizedToolCall;\n}\n\n/**\n * Error entry\n */\nexport interface ErrorEntry {\n type: 'error';\n message: string;\n code?: string;\n}\n\n/**\n * Custom widget entry - rendered via consumer-provided render function\n *\n * Use this for interactive UI components like:\n * - Image grid selectors\n * - Product carousels\n * - Form inputs\n * - Data visualizations\n * - Any custom interactive widget\n *\n * @example\n * ```tsx\n * const entry: NormalizedEntry = {\n * id: 'widget-1',\n * entryType: {\n * type: 'widget',\n * widgetType: 'image-grid',\n * widgetData: { images: [...], selected: null },\n * },\n * content: 'Select an image',\n * };\n * ```\n */\nexport interface WidgetEntry {\n type: 'widget';\n /** Widget type identifier (e.g., 'image-grid', 'product-selector') */\n widgetType: string;\n /** Widget-specific data passed to the render function */\n widgetData: unknown;\n}\n\n/**\n * Discriminated union of all entry types\n */\nexport type NormalizedEntryType =\n | UserMessageEntry\n | AssistantMessageEntry\n | ThinkingEntry\n | ToolCallEntry\n | ErrorEntry\n | WidgetEntry;\n\n// =============================================================================\n// Normalized Entry\n// =============================================================================\n\n/**\n * A normalized conversation entry for display\n */\nexport interface NormalizedEntry {\n /** Unique entry ID */\n id: string;\n\n /** ISO timestamp */\n timestamp?: string;\n\n /** Entry type with type-specific data */\n entryType: NormalizedEntryType;\n\n /** Text content for display */\n content: string;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\nexport function isCommandRunAction(action: ActionType): action is CommandRunAction {\n return action.action === 'command_run';\n}\n\nexport function isFileReadAction(action: ActionType): action is FileReadAction {\n return action.action === 'file_read';\n}\n\nexport function isFileEditAction(action: ActionType): action is FileEditAction {\n return action.action === 'file_edit';\n}\n\nexport function isFileWriteAction(action: ActionType): action is FileWriteAction {\n return action.action === 'file_write';\n}\n\nexport function isSearchAction(action: ActionType): action is SearchAction {\n return action.action === 'search';\n}\n\nexport function isGlobAction(action: ActionType): action is GlobAction {\n return action.action === 'glob';\n}\n\nexport function isWebFetchAction(action: ActionType): action is WebFetchAction {\n return action.action === 'web_fetch';\n}\n\nexport function isWebSearchAction(action: ActionType): action is WebSearchAction {\n return action.action === 'web_search';\n}\n\nexport function isMcpToolAction(action: ActionType): action is McpToolAction {\n return action.action === 'mcp_tool';\n}\n\nexport function isGenericToolAction(action: ActionType): action is GenericToolAction {\n return action.action === 'generic_tool';\n}\n\nexport function isTodoWriteAction(action: ActionType): action is TodoWriteAction {\n return action.action === 'todo_write';\n}\n\nexport function isAgentToolAction(action: ActionType): action is AgentToolAction {\n return action.action === 'agent_tool';\n}\n\nexport function isToolCallEntry(entry: NormalizedEntryType): entry is ToolCallEntry {\n return entry.type === 'tool_call';\n}\n\nexport function isErrorEntry(entry: NormalizedEntryType): entry is ErrorEntry {\n return entry.type === 'error';\n}\n\nexport function isWidgetEntry(entry: NormalizedEntryType): entry is WidgetEntry {\n return entry.type === 'widget';\n}\n\n// =============================================================================\n// Widget Rendering\n// =============================================================================\n\n/**\n * Props passed to the widget render function\n */\nexport interface WidgetRenderProps<T = unknown> {\n /** The widget entry being rendered */\n entry: NormalizedEntry;\n /** Widget type identifier */\n widgetType: string;\n /** Widget-specific data */\n widgetData: T;\n /** Callback for widget interactions (e.g., selection, submit) */\n onAction?: (action: WidgetAction) => void;\n}\n\n/**\n * Action dispatched from a widget interaction\n */\nexport interface WidgetAction {\n /** Action type (e.g., 'select', 'submit', 'cancel') */\n type: string;\n /** Widget type that dispatched the action */\n widgetType: string;\n /** Entry ID of the widget */\n entryId: string;\n /** Action-specific payload */\n payload?: unknown;\n}\n\n/**\n * Function type for rendering custom widgets\n *\n * @example\n * ```tsx\n * const renderWidget: WidgetRenderFunction = ({ widgetType, widgetData, onAction }) => {\n * switch (widgetType) {\n * case 'image-grid':\n * return (\n * <ImageGrid\n * images={widgetData.images}\n * onSelect={(id) => onAction?.({ type: 'select', widgetType, entryId, payload: { id } })}\n * />\n * );\n * case 'product-selector':\n * return <ProductSelector data={widgetData} onAction={onAction} />;\n * default:\n * return null;\n * }\n * };\n *\n * <MessageList entries={entries} renderWidget={renderWidget} />\n * ```\n */\nexport type WidgetRenderFunction = (props: WidgetRenderProps) => ReactNode;\n\n// =============================================================================\n// Log Types (for SandboxLogsPanel)\n// =============================================================================\n\nexport type LogLevel = 'info' | 'warn' | 'error' | 'debug';\nexport type LogCategory = 'setup' | 'skills' | 'execution' | 'process' | 'startup';\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n category: LogCategory;\n message: string;\n data?: Record<string, unknown>;\n}\n\n// =============================================================================\n// File Attachment\n// =============================================================================\n\nexport interface FileAttachment {\n name: string;\n type: string;\n size: number;\n base64: string;\n}\n\n// =============================================================================\n// Display Mode Configuration\n// =============================================================================\n\n/**\n * Display mode for tool calls in the chat interface\n *\n * - 'inline': Each tool call rendered inline as expandable cards (current behavior)\n * - 'compact': Single animated status line that updates with each tool call,\n * with accordion to expand and see execution details. Text streams unbroken.\n * - 'accordion': Simpler step-based accordion list that accumulates tool calls\n * with icons, labels, and durations (like Vidra's ThinkingIndicator).\n */\nexport type ToolDisplayMode = 'inline' | 'compact' | 'accordion';\n\n/**\n * Configuration for tool display behavior\n */\nexport interface ToolDisplayConfig {\n /**\n * Display mode for tool calls\n * @default 'inline'\n */\n mode: ToolDisplayMode;\n\n /**\n * For 'compact' mode: break into a new group every N tool calls\n * Set to 0 or undefined to never break (all tool calls in single group)\n * @default 0\n */\n breakEveryNToolCalls?: number;\n\n /**\n * Whether tool call groups start expanded\n * @default false\n */\n defaultExpanded?: boolean;\n\n /**\n * Animation duration for status line transitions (ms)\n * @default 300\n */\n animationDuration?: number;\n}\n\n/**\n * Default display configuration\n */\nexport const DEFAULT_DISPLAY_CONFIG: ToolDisplayConfig = {\n mode: 'inline',\n breakEveryNToolCalls: 0,\n defaultExpanded: false,\n animationDuration: 300,\n};\n\n// =============================================================================\n// Tool Execution Group (for compact mode)\n// =============================================================================\n\n/**\n * A group of consecutive tool calls that are displayed together\n * in compact mode. Text content before/after flows around the group.\n */\nexport interface ToolExecutionGroup {\n /** Unique ID for the group */\n id: string;\n\n /** Tool calls in this group (in order) */\n toolCalls: NormalizedToolCall[];\n\n /** Index of the currently active/latest tool call for status display */\n activeIndex: number;\n\n /** Whether all tool calls in this group have completed */\n isComplete: boolean;\n\n /** Overall status of the group */\n status: 'pending' | 'success' | 'partial_failure' | 'failed';\n}\n"]}
package/dist/utils.cjs CHANGED
@@ -105,6 +105,16 @@ function mapToolToActionType(toolName, input) {
105
105
  stats
106
106
  };
107
107
  }
108
+ case "Task": {
109
+ return {
110
+ action: "agent_tool",
111
+ agentType: inputObj.subagent_type || "general-purpose",
112
+ description: inputObj.description || "",
113
+ prompt: inputObj.prompt,
114
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
115
+ toolCallCount: 0
116
+ };
117
+ }
108
118
  default: {
109
119
  const mcpParts = parseMcpToolName(toolName);
110
120
  if (mcpParts) {
@@ -154,6 +164,8 @@ function generateToolSummary(_toolName, _input, actionType) {
154
164
  }
155
165
  return `${actionType.todos.length} tasks`;
156
166
  }
167
+ case "agent_tool":
168
+ return actionType.description;
157
169
  default:
158
170
  return "Unknown tool";
159
171
  }
@@ -221,6 +233,7 @@ function createToolCall(toolUse) {
221
233
  actionType,
222
234
  status: "pending",
223
235
  summary,
236
+ input: toolUse.input,
224
237
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
225
238
  };
226
239
  }
@@ -230,6 +243,7 @@ function updateToolCallWithResult(toolCall, content, isError) {
230
243
  updatedToolCall.status = isError ? "failed" : "success";
231
244
  updatedToolCall.completedAt = (/* @__PURE__ */ new Date()).toISOString();
232
245
  updatedToolCall.isError = isError;
246
+ updatedToolCall.output = content;
233
247
  if (actionType.action === "command_run") {
234
248
  const result = parseCommandResult(content);
235
249
  actionType.result = result;
@@ -267,6 +281,8 @@ function getActionIcon(actionType) {
267
281
  return "tool";
268
282
  case "todo_write":
269
283
  return "list-checks";
284
+ case "agent_tool":
285
+ return "bot";
270
286
  default:
271
287
  return "tool";
272
288
  }
@@ -295,6 +311,8 @@ function getActionLabel(actionType) {
295
311
  return "Tool";
296
312
  case "todo_write":
297
313
  return "Tasks";
314
+ case "agent_tool":
315
+ return actionType.agentType;
298
316
  default:
299
317
  return "Tool";
300
318
  }
@@ -317,6 +335,21 @@ function formatTimestamp(timestamp) {
317
335
  return timestamp;
318
336
  }
319
337
  }
338
+ function formatElapsedTime(startTime, endTime) {
339
+ const start = typeof startTime === "string" ? new Date(startTime) : startTime;
340
+ const end = endTime ? typeof endTime === "string" ? new Date(endTime) : endTime : /* @__PURE__ */ new Date();
341
+ const elapsedMs = end.getTime() - start.getTime();
342
+ const elapsedSeconds = Math.floor(elapsedMs / 1e3);
343
+ if (elapsedSeconds < 60) {
344
+ return `${elapsedSeconds}s`;
345
+ }
346
+ const minutes = Math.floor(elapsedSeconds / 60);
347
+ const seconds = elapsedSeconds % 60;
348
+ if (seconds === 0) {
349
+ return `${minutes}m`;
350
+ }
351
+ return `${minutes}m ${seconds}s`;
352
+ }
320
353
  function truncate(str, maxLength) {
321
354
  if (str.length <= maxLength) return str;
322
355
  return str.substring(0, maxLength - 3) + "...";
@@ -411,6 +444,7 @@ exports.cn = cn;
411
444
  exports.createToolCall = createToolCall;
412
445
  exports.extractTextContent = extractTextContent;
413
446
  exports.extractToolCallsFromGroup = extractToolCallsFromGroup;
447
+ exports.formatElapsedTime = formatElapsedTime;
414
448
  exports.formatFileSize = formatFileSize;
415
449
  exports.formatTimestamp = formatTimestamp;
416
450
  exports.formatToolName = formatToolName;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts"],"names":[],"mappings":"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","file":"utils.cjs","sourcesContent":["/**\n * @ash-cloud/ash-ui - Utilities\n *\n * Utility functions for normalizing and formatting tool calls\n * for display in agentic UIs.\n */\n\nimport type {\n ActionType,\n ToolResult,\n CommandRunResult,\n NormalizedToolCall,\n NormalizedEntry,\n ToolDisplayConfig,\n TodoItem,\n} from './types';\n\n// =============================================================================\n// Tool Name Formatting\n// =============================================================================\n\n/**\n * Format MCP tool names from mcp__server__tool to mcp:server:tool\n */\nexport function formatToolName(name: string): string {\n if (name.startsWith('mcp__')) {\n const parts = name.split('__');\n if (parts.length >= 3) {\n return `mcp:${parts[1]}:${parts.slice(2).join(':')}`;\n }\n }\n return name;\n}\n\n/**\n * Parse MCP tool name to extract server and tool names\n */\nexport function parseMcpToolName(name: string): { serverName: string; toolName: string } | null {\n if (name.startsWith('mcp__')) {\n const parts = name.split('__');\n if (parts.length >= 3 && parts[1] !== undefined) {\n return {\n serverName: parts[1],\n toolName: parts.slice(2).join('__'),\n };\n }\n }\n return null;\n}\n\n// =============================================================================\n// Action Type Mapping\n// =============================================================================\n\n/**\n * Map a tool name and input to a structured ActionType\n */\nexport function mapToolToActionType(toolName: string, input: unknown): ActionType {\n const inputObj = (input as Record<string, unknown>) || {};\n\n switch (toolName) {\n case 'Bash':\n return {\n action: 'command_run',\n command: (inputObj.command as string) || '',\n description: inputObj.description as string | undefined,\n };\n\n case 'Read': {\n const limit = inputObj.limit as number | undefined;\n return {\n action: 'file_read',\n path: (inputObj.file_path as string) || '',\n offset: inputObj.offset as number | undefined,\n limit,\n linesRead: limit, // Use limit as approximate lines read if specified\n };\n }\n\n case 'Edit': {\n const oldStr = inputObj.old_string as string | undefined;\n const newStr = inputObj.new_string as string | undefined;\n // Calculate line diff stats\n const oldLines = oldStr ? oldStr.split('\\n').length : 0;\n const newLines = newStr ? newStr.split('\\n').length : 0;\n return {\n action: 'file_edit',\n path: (inputObj.file_path as string) || '',\n oldString: oldStr,\n newString: newStr,\n replaceAll: inputObj.replace_all as boolean | undefined,\n linesAdded: newLines,\n linesRemoved: oldLines,\n };\n }\n\n case 'Write': {\n const content = inputObj.content as string | undefined;\n return {\n action: 'file_write',\n path: (inputObj.file_path as string) || '',\n content,\n linesWritten: content ? content.split('\\n').length : undefined,\n };\n }\n\n case 'Grep':\n return {\n action: 'search',\n pattern: (inputObj.pattern as string) || '',\n path: inputObj.path as string | undefined,\n glob: inputObj.glob as string | undefined,\n type: inputObj.type as string | undefined,\n };\n\n case 'Glob':\n return {\n action: 'glob',\n pattern: (inputObj.pattern as string) || '',\n path: inputObj.path as string | undefined,\n };\n\n case 'WebFetch':\n return {\n action: 'web_fetch',\n url: (inputObj.url as string) || '',\n prompt: inputObj.prompt as string | undefined,\n };\n\n case 'WebSearch':\n return {\n action: 'web_search',\n query: (inputObj.query as string) || '',\n };\n\n case 'TodoWrite': {\n const todos = (inputObj.todos as TodoItem[]) || [];\n const stats = {\n total: todos.length,\n completed: todos.filter((t) => t.status === 'completed').length,\n inProgress: todos.filter((t) => t.status === 'in_progress').length,\n pending: todos.filter((t) => t.status === 'pending').length,\n };\n return {\n action: 'todo_write',\n todos,\n stats,\n };\n }\n\n default: {\n // Check if it's an MCP tool\n const mcpParts = parseMcpToolName(toolName);\n if (mcpParts) {\n return {\n action: 'mcp_tool',\n serverName: mcpParts.serverName,\n toolName: mcpParts.toolName,\n arguments: input,\n };\n }\n\n // Generic tool\n return {\n action: 'generic_tool',\n toolName: formatToolName(toolName),\n arguments: input,\n };\n }\n }\n}\n\n// =============================================================================\n// Summary Generation\n// =============================================================================\n\n/**\n * Generate a human-readable summary for a tool call\n */\nexport function generateToolSummary(\n _toolName: string,\n _input: unknown,\n actionType: ActionType\n): string {\n switch (actionType.action) {\n case 'command_run': {\n const cmd = actionType.command;\n return cmd.length > 60 ? cmd.substring(0, 57) + '...' : cmd;\n }\n\n case 'file_read':\n return actionType.path;\n\n case 'file_edit':\n return actionType.path;\n\n case 'file_write':\n return actionType.path;\n\n case 'search':\n return `${actionType.pattern}${actionType.path ? ` in ${actionType.path}` : ''}`;\n\n case 'glob':\n return actionType.pattern;\n\n case 'web_fetch':\n return actionType.url;\n\n case 'web_search':\n return actionType.query;\n\n case 'mcp_tool':\n return `${actionType.serverName}:${actionType.toolName}`;\n\n case 'generic_tool':\n return actionType.toolName;\n\n case 'todo_write': {\n const { stats } = actionType;\n if (stats) {\n return `${stats.completed}/${stats.total} completed`;\n }\n return `${actionType.todos.length} tasks`;\n }\n\n default:\n return 'Unknown tool';\n }\n}\n\n// =============================================================================\n// Result Normalization\n// =============================================================================\n\n/**\n * Extract text content from various content formats\n */\nexport function extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n\n if (Array.isArray(content)) {\n return content\n .filter((item): item is { text: string } => typeof item?.text === 'string')\n .map((item) => item.text)\n .join('\\n');\n }\n\n if (content && typeof content === 'object' && 'text' in content) {\n return String((content as { text: unknown }).text);\n }\n\n return JSON.stringify(content, null, 2);\n}\n\n/**\n * Normalize tool result content to markdown or JSON format\n */\nexport function normalizeToolResult(content: unknown): ToolResult {\n if (typeof content === 'string') {\n try {\n const parsed = JSON.parse(content);\n return { type: 'json', value: parsed };\n } catch {\n return { type: 'markdown', value: content };\n }\n }\n\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (item): item is { type: 'text'; text: string } =>\n item?.type === 'text' && typeof item.text === 'string'\n )\n .map((item) => item.text);\n\n if (texts.length > 0) {\n const joined = texts.join('\\n\\n');\n try {\n return { type: 'json', value: JSON.parse(joined) };\n } catch {\n return { type: 'markdown', value: joined };\n }\n }\n }\n\n return { type: 'json', value: content };\n}\n\n/**\n * Parse command result from tool result content\n */\nexport function parseCommandResult(content: unknown): CommandRunResult {\n const output = extractTextContent(content);\n\n if (typeof content === 'string') {\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed.exitCode === 'number') {\n return {\n exitCode: parsed.exitCode,\n output: parsed.output || output,\n success: parsed.exitCode === 0,\n };\n }\n } catch {\n // Not JSON, use raw output\n }\n }\n\n return {\n output,\n success: true,\n };\n}\n\n// =============================================================================\n// Tool Call Creation\n// =============================================================================\n\nexport interface ToolUseInput {\n id: string;\n name: string;\n input: unknown;\n}\n\n/**\n * Create a NormalizedToolCall from a tool_use content block\n */\nexport function createToolCall(toolUse: ToolUseInput): NormalizedToolCall {\n const actionType = mapToolToActionType(toolUse.name, toolUse.input);\n const summary = generateToolSummary(toolUse.name, toolUse.input, actionType);\n\n return {\n id: toolUse.id,\n toolName: toolUse.name,\n actionType,\n status: 'pending',\n summary,\n startedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Update a NormalizedToolCall with its result\n */\nexport function updateToolCallWithResult(\n toolCall: NormalizedToolCall,\n content: unknown,\n isError?: boolean\n): NormalizedToolCall {\n const updatedToolCall = { ...toolCall };\n const actionType = { ...toolCall.actionType };\n\n updatedToolCall.status = isError ? 'failed' : 'success';\n updatedToolCall.completedAt = new Date().toISOString();\n updatedToolCall.isError = isError;\n\n if (actionType.action === 'command_run') {\n const result = parseCommandResult(content);\n (actionType as typeof actionType).result = result;\n if (result.exitCode !== undefined && result.exitCode !== 0) {\n updatedToolCall.status = 'failed';\n updatedToolCall.isError = true;\n }\n } else if (actionType.action === 'mcp_tool' || actionType.action === 'generic_tool') {\n (actionType as typeof actionType).result = normalizeToolResult(content);\n }\n\n updatedToolCall.actionType = actionType;\n return updatedToolCall;\n}\n\n// =============================================================================\n// Display Helpers\n// =============================================================================\n\n/**\n * Get display icon name for an action type\n */\nexport function getActionIcon(actionType: ActionType): string {\n switch (actionType.action) {\n case 'command_run':\n return 'terminal';\n case 'file_read':\n return 'file-text';\n case 'file_edit':\n return 'edit';\n case 'file_write':\n return 'file-plus';\n case 'search':\n return 'search';\n case 'glob':\n return 'folder-search';\n case 'web_fetch':\n return 'globe';\n case 'web_search':\n return 'search';\n case 'mcp_tool':\n return 'plug';\n case 'generic_tool':\n return 'tool';\n case 'todo_write':\n return 'list-checks';\n default:\n return 'tool';\n }\n}\n\n/**\n * Get display label for an action type\n */\nexport function getActionLabel(actionType: ActionType): string {\n switch (actionType.action) {\n case 'command_run':\n return 'Command';\n case 'file_read':\n return 'Read';\n case 'file_edit':\n return 'Edit';\n case 'file_write':\n return 'Write';\n case 'search':\n return 'Search';\n case 'glob':\n return 'Glob';\n case 'web_fetch':\n return 'Fetch';\n case 'web_search':\n return 'Search';\n case 'mcp_tool':\n return 'MCP';\n case 'generic_tool':\n return 'Tool';\n case 'todo_write':\n return 'Tasks';\n default:\n return 'Tool';\n }\n}\n\n// =============================================================================\n// Formatting Helpers\n// =============================================================================\n\n/**\n * Format a file size in bytes to a human-readable string\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Format a timestamp to a locale time string\n */\nexport function formatTimestamp(timestamp: string): string {\n try {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n } catch {\n return timestamp;\n }\n}\n\n/**\n * Truncate a string with ellipsis\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.substring(0, maxLength - 3) + '...';\n}\n\n/**\n * Join class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n// =============================================================================\n// Entry Grouping for Compact Mode\n// =============================================================================\n\n/**\n * A grouped entry that contains either a single entry or a group of consecutive tool calls\n */\nexport type GroupedEntry =\n | { type: 'single'; entry: NormalizedEntry }\n | { type: 'tool_group'; entries: NormalizedEntry[]; id: string };\n\n/**\n * Group consecutive tool call entries together based on display config.\n *\n * This is used in compact mode to merge consecutive tool calls into\n * a single ToolExecutionGroup, while keeping text content separate.\n *\n * @param entries - Array of normalized entries\n * @param config - Display configuration (uses breakEveryNToolCalls)\n * @returns Array of grouped entries\n */\nexport function groupEntriesForCompactMode(\n entries: NormalizedEntry[],\n config: ToolDisplayConfig\n): GroupedEntry[] {\n const result: GroupedEntry[] = [];\n let currentToolGroup: NormalizedEntry[] = [];\n let toolGroupCounter = 0;\n\n const flushToolGroup = () => {\n if (currentToolGroup.length > 0) {\n result.push({\n type: 'tool_group',\n entries: [...currentToolGroup],\n id: `tool-group-${toolGroupCounter++}`,\n });\n currentToolGroup = [];\n }\n };\n\n for (const entry of entries) {\n if (entry.entryType.type === 'tool_call') {\n currentToolGroup.push(entry);\n\n // Check if we should break the group\n if (\n config.breakEveryNToolCalls &&\n config.breakEveryNToolCalls > 0 &&\n currentToolGroup.length >= config.breakEveryNToolCalls\n ) {\n flushToolGroup();\n }\n } else {\n // Non-tool-call entry - flush any pending tool group\n flushToolGroup();\n result.push({ type: 'single', entry });\n }\n }\n\n // Flush any remaining tool group\n flushToolGroup();\n\n return result;\n}\n\n/**\n * Extract tool calls from a group of entries\n */\nexport function extractToolCallsFromGroup(entries: NormalizedEntry[]): NormalizedToolCall[] {\n return entries\n .filter((e): e is NormalizedEntry & { entryType: { type: 'tool_call'; toolCall: NormalizedToolCall } } =>\n e.entryType.type === 'tool_call'\n )\n .map((e) => e.entryType.toolCall);\n}\n\n// =============================================================================\n// Option Parsing\n// =============================================================================\n\n/**\n * A parsed option from assistant message content\n */\nexport interface ParsedOption {\n /** Option identifier (e.g., \"1\", \"2\", \"A\", \"B\") */\n id: string;\n /** Option title/label */\n label: string;\n /** Optional description text */\n description?: string;\n}\n\n/**\n * Result of parsing options from content\n */\nexport interface ParsedOptionsResult {\n /** Text before the options */\n preamble: string;\n /** Parsed options */\n options: ParsedOption[];\n}\n\n/**\n * Parse options from assistant message content.\n * Detects patterns like:\n * - \"Option 1: Title\" / \"Option 2: Title\"\n * - \"**Option 1:** Title\" (bold markdown)\n * - Numbered lists with descriptions\n *\n * @returns ParsedOptionsResult if options found, null otherwise\n */\nexport function parseOptionsFromContent(content: string): ParsedOptionsResult | null {\n // Pattern 1: \"Option N:\" format (with or without bold markdown)\n // Matches: \"Option 1:\", \"**Option 1:**\", \"Option 1 -\", etc.\n const optionPattern = /(?:\\*\\*)?Option\\s+(\\d+)(?:\\*\\*)?[:\\-]\\s*([^\\n]+)(?:\\n((?:(?!\\n(?:\\*\\*)?Option\\s+\\d).)*?))?/gi;\n\n const options: ParsedOption[] = [];\n let firstMatchStart = -1;\n let match: RegExpExecArray | null;\n\n // Reset regex\n optionPattern.lastIndex = 0;\n\n while ((match = optionPattern.exec(content)) !== null) {\n if (firstMatchStart === -1) {\n firstMatchStart = match.index;\n }\n\n const id = match[1] ?? '';\n const labelRaw = match[2];\n const label = labelRaw ? labelRaw.trim() : '';\n // Description is everything after the label until the next option\n const descriptionRaw = match[3];\n\n // Clean up description - remove leading/trailing whitespace and normalize\n let description: string | undefined;\n if (descriptionRaw) {\n const cleaned = descriptionRaw\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0)\n .join(' ');\n if (cleaned) {\n description = cleaned;\n }\n }\n\n options.push({ id, label, description });\n }\n\n // Need at least 2 options to be considered a valid options list\n if (options.length >= 2) {\n const preamble = firstMatchStart > 0\n ? content.substring(0, firstMatchStart).trim()\n : '';\n\n return { preamble, options };\n }\n\n // Pattern 2: Simple numbered list at end of message\n // Look for \"What would you like to do?\" or similar followed by numbered items\n const questionPattern = /^(.*?(?:what would you like to do\\??|choose an option|select.*?:|here are your options.*?:))\\s*\\n+((?:\\d+\\.\\s+.+\\n?)+)/ims;\n const questionMatch = content.match(questionPattern);\n\n if (questionMatch && questionMatch[1] && questionMatch[2]) {\n const preamble = questionMatch[1].trim();\n const listSection = questionMatch[2];\n\n // Parse numbered list items\n const listPattern = /(\\d+)\\.\\s+([^\\n]+)/g;\n let listMatch: RegExpExecArray | null;\n\n while ((listMatch = listPattern.exec(listSection)) !== null) {\n const listId = listMatch[1] ?? '';\n const listLabelRaw = listMatch[2];\n const listLabel = listLabelRaw ? listLabelRaw.trim() : '';\n options.push({\n id: listId,\n label: listLabel,\n });\n }\n\n if (options.length >= 2) {\n return { preamble, options };\n }\n }\n\n return null;\n}\n"]}
1
+ {"version":3,"sources":["../src/utils.ts"],"names":[],"mappings":"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","file":"utils.cjs","sourcesContent":["/**\n * @ash-cloud/ash-ui - Utilities\n *\n * Utility functions for normalizing and formatting tool calls\n * for display in agentic UIs.\n */\n\nimport type {\n ActionType,\n ToolResult,\n CommandRunResult,\n NormalizedToolCall,\n NormalizedEntry,\n ToolDisplayConfig,\n TodoItem,\n AgentToolAction,\n} from './types';\n\n// =============================================================================\n// Tool Name Formatting\n// =============================================================================\n\n/**\n * Format MCP tool names from mcp__server__tool to mcp:server:tool\n */\nexport function formatToolName(name: string): string {\n if (name.startsWith('mcp__')) {\n const parts = name.split('__');\n if (parts.length >= 3) {\n return `mcp:${parts[1]}:${parts.slice(2).join(':')}`;\n }\n }\n return name;\n}\n\n/**\n * Parse MCP tool name to extract server and tool names\n */\nexport function parseMcpToolName(name: string): { serverName: string; toolName: string } | null {\n if (name.startsWith('mcp__')) {\n const parts = name.split('__');\n if (parts.length >= 3 && parts[1] !== undefined) {\n return {\n serverName: parts[1],\n toolName: parts.slice(2).join('__'),\n };\n }\n }\n return null;\n}\n\n// =============================================================================\n// Action Type Mapping\n// =============================================================================\n\n/**\n * Map a tool name and input to a structured ActionType\n */\nexport function mapToolToActionType(toolName: string, input: unknown): ActionType {\n const inputObj = (input as Record<string, unknown>) || {};\n\n switch (toolName) {\n case 'Bash':\n return {\n action: 'command_run',\n command: (inputObj.command as string) || '',\n description: inputObj.description as string | undefined,\n };\n\n case 'Read': {\n const limit = inputObj.limit as number | undefined;\n return {\n action: 'file_read',\n path: (inputObj.file_path as string) || '',\n offset: inputObj.offset as number | undefined,\n limit,\n linesRead: limit, // Use limit as approximate lines read if specified\n };\n }\n\n case 'Edit': {\n const oldStr = inputObj.old_string as string | undefined;\n const newStr = inputObj.new_string as string | undefined;\n // Calculate line diff stats\n const oldLines = oldStr ? oldStr.split('\\n').length : 0;\n const newLines = newStr ? newStr.split('\\n').length : 0;\n return {\n action: 'file_edit',\n path: (inputObj.file_path as string) || '',\n oldString: oldStr,\n newString: newStr,\n replaceAll: inputObj.replace_all as boolean | undefined,\n linesAdded: newLines,\n linesRemoved: oldLines,\n };\n }\n\n case 'Write': {\n const content = inputObj.content as string | undefined;\n return {\n action: 'file_write',\n path: (inputObj.file_path as string) || '',\n content,\n linesWritten: content ? content.split('\\n').length : undefined,\n };\n }\n\n case 'Grep':\n return {\n action: 'search',\n pattern: (inputObj.pattern as string) || '',\n path: inputObj.path as string | undefined,\n glob: inputObj.glob as string | undefined,\n type: inputObj.type as string | undefined,\n };\n\n case 'Glob':\n return {\n action: 'glob',\n pattern: (inputObj.pattern as string) || '',\n path: inputObj.path as string | undefined,\n };\n\n case 'WebFetch':\n return {\n action: 'web_fetch',\n url: (inputObj.url as string) || '',\n prompt: inputObj.prompt as string | undefined,\n };\n\n case 'WebSearch':\n return {\n action: 'web_search',\n query: (inputObj.query as string) || '',\n };\n\n case 'TodoWrite': {\n const todos = (inputObj.todos as TodoItem[]) || [];\n const stats = {\n total: todos.length,\n completed: todos.filter((t) => t.status === 'completed').length,\n inProgress: todos.filter((t) => t.status === 'in_progress').length,\n pending: todos.filter((t) => t.status === 'pending').length,\n };\n return {\n action: 'todo_write',\n todos,\n stats,\n };\n }\n\n case 'Task': {\n return {\n action: 'agent_tool',\n agentType: (inputObj.subagent_type as string) || 'general-purpose',\n description: (inputObj.description as string) || '',\n prompt: inputObj.prompt as string | undefined,\n startedAt: new Date().toISOString(),\n toolCallCount: 0,\n };\n }\n\n default: {\n // Check if it's an MCP tool\n const mcpParts = parseMcpToolName(toolName);\n if (mcpParts) {\n return {\n action: 'mcp_tool',\n serverName: mcpParts.serverName,\n toolName: mcpParts.toolName,\n arguments: input,\n };\n }\n\n // Generic tool\n return {\n action: 'generic_tool',\n toolName: formatToolName(toolName),\n arguments: input,\n };\n }\n }\n}\n\n// =============================================================================\n// Summary Generation\n// =============================================================================\n\n/**\n * Generate a human-readable summary for a tool call\n */\nexport function generateToolSummary(\n _toolName: string,\n _input: unknown,\n actionType: ActionType\n): string {\n switch (actionType.action) {\n case 'command_run': {\n const cmd = actionType.command;\n return cmd.length > 60 ? cmd.substring(0, 57) + '...' : cmd;\n }\n\n case 'file_read':\n return actionType.path;\n\n case 'file_edit':\n return actionType.path;\n\n case 'file_write':\n return actionType.path;\n\n case 'search':\n return `${actionType.pattern}${actionType.path ? ` in ${actionType.path}` : ''}`;\n\n case 'glob':\n return actionType.pattern;\n\n case 'web_fetch':\n return actionType.url;\n\n case 'web_search':\n return actionType.query;\n\n case 'mcp_tool':\n return `${actionType.serverName}:${actionType.toolName}`;\n\n case 'generic_tool':\n return actionType.toolName;\n\n case 'todo_write': {\n const { stats } = actionType;\n if (stats) {\n return `${stats.completed}/${stats.total} completed`;\n }\n return `${actionType.todos.length} tasks`;\n }\n\n case 'agent_tool':\n return actionType.description;\n\n default:\n return 'Unknown tool';\n }\n}\n\n// =============================================================================\n// Result Normalization\n// =============================================================================\n\n/**\n * Extract text content from various content formats\n */\nexport function extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n\n if (Array.isArray(content)) {\n return content\n .filter((item): item is { text: string } => typeof item?.text === 'string')\n .map((item) => item.text)\n .join('\\n');\n }\n\n if (content && typeof content === 'object' && 'text' in content) {\n return String((content as { text: unknown }).text);\n }\n\n return JSON.stringify(content, null, 2);\n}\n\n/**\n * Normalize tool result content to markdown or JSON format\n */\nexport function normalizeToolResult(content: unknown): ToolResult {\n if (typeof content === 'string') {\n try {\n const parsed = JSON.parse(content);\n return { type: 'json', value: parsed };\n } catch {\n return { type: 'markdown', value: content };\n }\n }\n\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (item): item is { type: 'text'; text: string } =>\n item?.type === 'text' && typeof item.text === 'string'\n )\n .map((item) => item.text);\n\n if (texts.length > 0) {\n const joined = texts.join('\\n\\n');\n try {\n return { type: 'json', value: JSON.parse(joined) };\n } catch {\n return { type: 'markdown', value: joined };\n }\n }\n }\n\n return { type: 'json', value: content };\n}\n\n/**\n * Parse command result from tool result content\n */\nexport function parseCommandResult(content: unknown): CommandRunResult {\n const output = extractTextContent(content);\n\n if (typeof content === 'string') {\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed.exitCode === 'number') {\n return {\n exitCode: parsed.exitCode,\n output: parsed.output || output,\n success: parsed.exitCode === 0,\n };\n }\n } catch {\n // Not JSON, use raw output\n }\n }\n\n return {\n output,\n success: true,\n };\n}\n\n// =============================================================================\n// Tool Call Creation\n// =============================================================================\n\nexport interface ToolUseInput {\n id: string;\n name: string;\n input: unknown;\n}\n\n/**\n * Create a NormalizedToolCall from a tool_use content block\n */\nexport function createToolCall(toolUse: ToolUseInput): NormalizedToolCall {\n const actionType = mapToolToActionType(toolUse.name, toolUse.input);\n const summary = generateToolSummary(toolUse.name, toolUse.input, actionType);\n\n return {\n id: toolUse.id,\n toolName: toolUse.name,\n actionType,\n status: 'pending',\n summary,\n input: toolUse.input,\n startedAt: new Date().toISOString(),\n };\n}\n\n/**\n * Update a NormalizedToolCall with its result\n */\nexport function updateToolCallWithResult(\n toolCall: NormalizedToolCall,\n content: unknown,\n isError?: boolean\n): NormalizedToolCall {\n const updatedToolCall = { ...toolCall };\n const actionType = { ...toolCall.actionType };\n\n updatedToolCall.status = isError ? 'failed' : 'success';\n updatedToolCall.completedAt = new Date().toISOString();\n updatedToolCall.isError = isError;\n updatedToolCall.output = content;\n\n if (actionType.action === 'command_run') {\n const result = parseCommandResult(content);\n (actionType as typeof actionType).result = result;\n if (result.exitCode !== undefined && result.exitCode !== 0) {\n updatedToolCall.status = 'failed';\n updatedToolCall.isError = true;\n }\n } else if (actionType.action === 'mcp_tool' || actionType.action === 'generic_tool') {\n (actionType as typeof actionType).result = normalizeToolResult(content);\n }\n\n updatedToolCall.actionType = actionType;\n return updatedToolCall;\n}\n\n// =============================================================================\n// Display Helpers\n// =============================================================================\n\n/**\n * Get display icon name for an action type\n */\nexport function getActionIcon(actionType: ActionType): string {\n switch (actionType.action) {\n case 'command_run':\n return 'terminal';\n case 'file_read':\n return 'file-text';\n case 'file_edit':\n return 'edit';\n case 'file_write':\n return 'file-plus';\n case 'search':\n return 'search';\n case 'glob':\n return 'folder-search';\n case 'web_fetch':\n return 'globe';\n case 'web_search':\n return 'search';\n case 'mcp_tool':\n return 'plug';\n case 'generic_tool':\n return 'tool';\n case 'todo_write':\n return 'list-checks';\n case 'agent_tool':\n return 'bot';\n default:\n return 'tool';\n }\n}\n\n/**\n * Get display label for an action type\n */\nexport function getActionLabel(actionType: ActionType): string {\n switch (actionType.action) {\n case 'command_run':\n return 'Command';\n case 'file_read':\n return 'Read';\n case 'file_edit':\n return 'Edit';\n case 'file_write':\n return 'Write';\n case 'search':\n return 'Search';\n case 'glob':\n return 'Glob';\n case 'web_fetch':\n return 'Fetch';\n case 'web_search':\n return 'Search';\n case 'mcp_tool':\n return 'MCP';\n case 'generic_tool':\n return 'Tool';\n case 'todo_write':\n return 'Tasks';\n case 'agent_tool':\n return (actionType as AgentToolAction).agentType;\n default:\n return 'Tool';\n }\n}\n\n// =============================================================================\n// Formatting Helpers\n// =============================================================================\n\n/**\n * Format a file size in bytes to a human-readable string\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Format a timestamp to a locale time string\n */\nexport function formatTimestamp(timestamp: string): string {\n try {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n } catch {\n return timestamp;\n }\n}\n\n/**\n * Format elapsed time in human-readable format\n * @param startTime - ISO timestamp or Date when the operation started\n * @param endTime - Optional ISO timestamp or Date when the operation ended (defaults to now)\n * @returns Formatted string like \"2s\", \"1m 30s\", \"2m\", etc.\n */\nexport function formatElapsedTime(startTime: string | Date, endTime?: string | Date): string {\n const start = typeof startTime === 'string' ? new Date(startTime) : startTime;\n const end = endTime ? (typeof endTime === 'string' ? new Date(endTime) : endTime) : new Date();\n\n const elapsedMs = end.getTime() - start.getTime();\n const elapsedSeconds = Math.floor(elapsedMs / 1000);\n\n if (elapsedSeconds < 60) {\n return `${elapsedSeconds}s`;\n }\n\n const minutes = Math.floor(elapsedSeconds / 60);\n const seconds = elapsedSeconds % 60;\n\n if (seconds === 0) {\n return `${minutes}m`;\n }\n\n return `${minutes}m ${seconds}s`;\n}\n\n/**\n * Truncate a string with ellipsis\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.substring(0, maxLength - 3) + '...';\n}\n\n/**\n * Join class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n// =============================================================================\n// Entry Grouping for Compact Mode\n// =============================================================================\n\n/**\n * A grouped entry that contains either a single entry or a group of consecutive tool calls\n */\nexport type GroupedEntry =\n | { type: 'single'; entry: NormalizedEntry }\n | { type: 'tool_group'; entries: NormalizedEntry[]; id: string };\n\n/**\n * Group consecutive tool call entries together based on display config.\n *\n * This is used in compact mode to merge consecutive tool calls into\n * a single ToolExecutionGroup, while keeping text content separate.\n *\n * @param entries - Array of normalized entries\n * @param config - Display configuration (uses breakEveryNToolCalls)\n * @returns Array of grouped entries\n */\nexport function groupEntriesForCompactMode(\n entries: NormalizedEntry[],\n config: ToolDisplayConfig\n): GroupedEntry[] {\n const result: GroupedEntry[] = [];\n let currentToolGroup: NormalizedEntry[] = [];\n let toolGroupCounter = 0;\n\n const flushToolGroup = () => {\n if (currentToolGroup.length > 0) {\n result.push({\n type: 'tool_group',\n entries: [...currentToolGroup],\n id: `tool-group-${toolGroupCounter++}`,\n });\n currentToolGroup = [];\n }\n };\n\n for (const entry of entries) {\n if (entry.entryType.type === 'tool_call') {\n currentToolGroup.push(entry);\n\n // Check if we should break the group\n if (\n config.breakEveryNToolCalls &&\n config.breakEveryNToolCalls > 0 &&\n currentToolGroup.length >= config.breakEveryNToolCalls\n ) {\n flushToolGroup();\n }\n } else {\n // Non-tool-call entry - flush any pending tool group\n flushToolGroup();\n result.push({ type: 'single', entry });\n }\n }\n\n // Flush any remaining tool group\n flushToolGroup();\n\n return result;\n}\n\n/**\n * Extract tool calls from a group of entries\n */\nexport function extractToolCallsFromGroup(entries: NormalizedEntry[]): NormalizedToolCall[] {\n return entries\n .filter((e): e is NormalizedEntry & { entryType: { type: 'tool_call'; toolCall: NormalizedToolCall } } =>\n e.entryType.type === 'tool_call'\n )\n .map((e) => e.entryType.toolCall);\n}\n\n// =============================================================================\n// Option Parsing\n// =============================================================================\n\n/**\n * A parsed option from assistant message content\n */\nexport interface ParsedOption {\n /** Option identifier (e.g., \"1\", \"2\", \"A\", \"B\") */\n id: string;\n /** Option title/label */\n label: string;\n /** Optional description text */\n description?: string;\n}\n\n/**\n * Result of parsing options from content\n */\nexport interface ParsedOptionsResult {\n /** Text before the options */\n preamble: string;\n /** Parsed options */\n options: ParsedOption[];\n}\n\n/**\n * Parse options from assistant message content.\n * Detects patterns like:\n * - \"Option 1: Title\" / \"Option 2: Title\"\n * - \"**Option 1:** Title\" (bold markdown)\n * - Numbered lists with descriptions\n *\n * @returns ParsedOptionsResult if options found, null otherwise\n */\nexport function parseOptionsFromContent(content: string): ParsedOptionsResult | null {\n // Pattern 1: \"Option N:\" format (with or without bold markdown)\n // Matches: \"Option 1:\", \"**Option 1:**\", \"Option 1 -\", etc.\n const optionPattern = /(?:\\*\\*)?Option\\s+(\\d+)(?:\\*\\*)?[:\\-]\\s*([^\\n]+)(?:\\n((?:(?!\\n(?:\\*\\*)?Option\\s+\\d).)*?))?/gi;\n\n const options: ParsedOption[] = [];\n let firstMatchStart = -1;\n let match: RegExpExecArray | null;\n\n // Reset regex\n optionPattern.lastIndex = 0;\n\n while ((match = optionPattern.exec(content)) !== null) {\n if (firstMatchStart === -1) {\n firstMatchStart = match.index;\n }\n\n const id = match[1] ?? '';\n const labelRaw = match[2];\n const label = labelRaw ? labelRaw.trim() : '';\n // Description is everything after the label until the next option\n const descriptionRaw = match[3];\n\n // Clean up description - remove leading/trailing whitespace and normalize\n let description: string | undefined;\n if (descriptionRaw) {\n const cleaned = descriptionRaw\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0)\n .join(' ');\n if (cleaned) {\n description = cleaned;\n }\n }\n\n options.push({ id, label, description });\n }\n\n // Need at least 2 options to be considered a valid options list\n if (options.length >= 2) {\n const preamble = firstMatchStart > 0\n ? content.substring(0, firstMatchStart).trim()\n : '';\n\n return { preamble, options };\n }\n\n // Pattern 2: Simple numbered list at end of message\n // Look for \"What would you like to do?\" or similar followed by numbered items\n const questionPattern = /^(.*?(?:what would you like to do\\??|choose an option|select.*?:|here are your options.*?:))\\s*\\n+((?:\\d+\\.\\s+.+\\n?)+)/ims;\n const questionMatch = content.match(questionPattern);\n\n if (questionMatch && questionMatch[1] && questionMatch[2]) {\n const preamble = questionMatch[1].trim();\n const listSection = questionMatch[2];\n\n // Parse numbered list items\n const listPattern = /(\\d+)\\.\\s+([^\\n]+)/g;\n let listMatch: RegExpExecArray | null;\n\n while ((listMatch = listPattern.exec(listSection)) !== null) {\n const listId = listMatch[1] ?? '';\n const listLabelRaw = listMatch[2];\n const listLabel = listLabelRaw ? listLabelRaw.trim() : '';\n options.push({\n id: listId,\n label: listLabel,\n });\n }\n\n if (options.length >= 2) {\n return { preamble, options };\n }\n }\n\n return null;\n}\n"]}
package/dist/utils.d.cts CHANGED
@@ -68,6 +68,13 @@ declare function formatFileSize(bytes: number): string;
68
68
  * Format a timestamp to a locale time string
69
69
  */
70
70
  declare function formatTimestamp(timestamp: string): string;
71
+ /**
72
+ * Format elapsed time in human-readable format
73
+ * @param startTime - ISO timestamp or Date when the operation started
74
+ * @param endTime - Optional ISO timestamp or Date when the operation ended (defaults to now)
75
+ * @returns Formatted string like "2s", "1m 30s", "2m", etc.
76
+ */
77
+ declare function formatElapsedTime(startTime: string | Date, endTime?: string | Date): string;
71
78
  /**
72
79
  * Truncate a string with ellipsis
73
80
  */
@@ -133,4 +140,4 @@ interface ParsedOptionsResult {
133
140
  */
134
141
  declare function parseOptionsFromContent(content: string): ParsedOptionsResult | null;
135
142
 
136
- export { type GroupedEntry, type ParsedOption, type ParsedOptionsResult, type ToolUseInput, cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };
143
+ export { type GroupedEntry, type ParsedOption, type ParsedOptionsResult, type ToolUseInput, cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatElapsedTime, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };
package/dist/utils.d.ts CHANGED
@@ -68,6 +68,13 @@ declare function formatFileSize(bytes: number): string;
68
68
  * Format a timestamp to a locale time string
69
69
  */
70
70
  declare function formatTimestamp(timestamp: string): string;
71
+ /**
72
+ * Format elapsed time in human-readable format
73
+ * @param startTime - ISO timestamp or Date when the operation started
74
+ * @param endTime - Optional ISO timestamp or Date when the operation ended (defaults to now)
75
+ * @returns Formatted string like "2s", "1m 30s", "2m", etc.
76
+ */
77
+ declare function formatElapsedTime(startTime: string | Date, endTime?: string | Date): string;
71
78
  /**
72
79
  * Truncate a string with ellipsis
73
80
  */
@@ -133,4 +140,4 @@ interface ParsedOptionsResult {
133
140
  */
134
141
  declare function parseOptionsFromContent(content: string): ParsedOptionsResult | null;
135
142
 
136
- export { type GroupedEntry, type ParsedOption, type ParsedOptionsResult, type ToolUseInput, cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };
143
+ export { type GroupedEntry, type ParsedOption, type ParsedOptionsResult, type ToolUseInput, cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatElapsedTime, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };
package/dist/utils.js CHANGED
@@ -103,6 +103,16 @@ function mapToolToActionType(toolName, input) {
103
103
  stats
104
104
  };
105
105
  }
106
+ case "Task": {
107
+ return {
108
+ action: "agent_tool",
109
+ agentType: inputObj.subagent_type || "general-purpose",
110
+ description: inputObj.description || "",
111
+ prompt: inputObj.prompt,
112
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
113
+ toolCallCount: 0
114
+ };
115
+ }
106
116
  default: {
107
117
  const mcpParts = parseMcpToolName(toolName);
108
118
  if (mcpParts) {
@@ -152,6 +162,8 @@ function generateToolSummary(_toolName, _input, actionType) {
152
162
  }
153
163
  return `${actionType.todos.length} tasks`;
154
164
  }
165
+ case "agent_tool":
166
+ return actionType.description;
155
167
  default:
156
168
  return "Unknown tool";
157
169
  }
@@ -219,6 +231,7 @@ function createToolCall(toolUse) {
219
231
  actionType,
220
232
  status: "pending",
221
233
  summary,
234
+ input: toolUse.input,
222
235
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
223
236
  };
224
237
  }
@@ -228,6 +241,7 @@ function updateToolCallWithResult(toolCall, content, isError) {
228
241
  updatedToolCall.status = isError ? "failed" : "success";
229
242
  updatedToolCall.completedAt = (/* @__PURE__ */ new Date()).toISOString();
230
243
  updatedToolCall.isError = isError;
244
+ updatedToolCall.output = content;
231
245
  if (actionType.action === "command_run") {
232
246
  const result = parseCommandResult(content);
233
247
  actionType.result = result;
@@ -265,6 +279,8 @@ function getActionIcon(actionType) {
265
279
  return "tool";
266
280
  case "todo_write":
267
281
  return "list-checks";
282
+ case "agent_tool":
283
+ return "bot";
268
284
  default:
269
285
  return "tool";
270
286
  }
@@ -293,6 +309,8 @@ function getActionLabel(actionType) {
293
309
  return "Tool";
294
310
  case "todo_write":
295
311
  return "Tasks";
312
+ case "agent_tool":
313
+ return actionType.agentType;
296
314
  default:
297
315
  return "Tool";
298
316
  }
@@ -315,6 +333,21 @@ function formatTimestamp(timestamp) {
315
333
  return timestamp;
316
334
  }
317
335
  }
336
+ function formatElapsedTime(startTime, endTime) {
337
+ const start = typeof startTime === "string" ? new Date(startTime) : startTime;
338
+ const end = endTime ? typeof endTime === "string" ? new Date(endTime) : endTime : /* @__PURE__ */ new Date();
339
+ const elapsedMs = end.getTime() - start.getTime();
340
+ const elapsedSeconds = Math.floor(elapsedMs / 1e3);
341
+ if (elapsedSeconds < 60) {
342
+ return `${elapsedSeconds}s`;
343
+ }
344
+ const minutes = Math.floor(elapsedSeconds / 60);
345
+ const seconds = elapsedSeconds % 60;
346
+ if (seconds === 0) {
347
+ return `${minutes}m`;
348
+ }
349
+ return `${minutes}m ${seconds}s`;
350
+ }
318
351
  function truncate(str, maxLength) {
319
352
  if (str.length <= maxLength) return str;
320
353
  return str.substring(0, maxLength - 3) + "...";
@@ -405,6 +438,6 @@ function parseOptionsFromContent(content) {
405
438
  return null;
406
439
  }
407
440
 
408
- export { cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };
441
+ export { cn, createToolCall, extractTextContent, extractToolCallsFromGroup, formatElapsedTime, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, truncate, updateToolCallWithResult };
409
442
  //# sourceMappingURL=utils.js.map
410
443
  //# sourceMappingURL=utils.js.map