@within-7/minto 0.2.0 → 0.3.0
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/commands/agents/AgentsCommand.js +22 -24
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/context.js +2 -1
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/export.js +2 -1
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/mcp-interactive.js +7 -6
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +3 -2
- package/dist/commands/model.js.map +2 -2
- package/dist/commands/permissions.js +4 -3
- package/dist/commands/permissions.js.map +2 -2
- package/dist/commands/plugin/AddMarketplaceForm.js +3 -2
- package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
- package/dist/commands/plugin/ConfirmDialog.js +2 -1
- package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
- package/dist/commands/plugin/ErrorView.js +2 -1
- package/dist/commands/plugin/ErrorView.js.map +2 -2
- package/dist/commands/plugin/InstalledPluginsByMarketplace.js +5 -4
- package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
- package/dist/commands/plugin/InstalledPluginsManager.js +5 -4
- package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
- package/dist/commands/plugin/MainMenu.js +2 -1
- package/dist/commands/plugin/MainMenu.js.map +2 -2
- package/dist/commands/plugin/MarketplaceManager.js +5 -4
- package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
- package/dist/commands/plugin/MarketplaceSelector.js +4 -3
- package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
- package/dist/commands/plugin/PlaceholderScreen.js +3 -2
- package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
- package/dist/commands/plugin/PluginBrowser.js +6 -5
- package/dist/commands/plugin/PluginBrowser.js.map +2 -2
- package/dist/commands/plugin/PluginDetailsInstall.js +5 -4
- package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
- package/dist/commands/plugin/PluginDetailsManage.js +4 -3
- package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
- package/dist/commands/plugin.js +16 -15
- package/dist/commands/plugin.js.map +2 -2
- package/dist/commands/sandbox.js +4 -3
- package/dist/commands/sandbox.js.map +2 -2
- package/dist/commands/setup.js +2 -1
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/status.js +2 -1
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +245 -0
- package/dist/commands/undo.js.map +7 -0
- package/dist/commands.js +2 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AgentThinkingBlock.js +1 -1
- package/dist/components/AgentThinkingBlock.js.map +2 -2
- package/dist/components/AsciiLogo.js +7 -8
- package/dist/components/AsciiLogo.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/QuestionView.js +2 -1
- package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
- package/dist/components/CollapsibleHint.js +2 -1
- package/dist/components/CollapsibleHint.js.map +2 -2
- package/dist/components/Config.js +3 -2
- package/dist/components/Config.js.map +2 -2
- package/dist/components/ConsoleOAuthFlow.js +2 -1
- package/dist/components/ConsoleOAuthFlow.js.map +2 -2
- package/dist/components/Cost.js +2 -1
- package/dist/components/Cost.js.map +2 -2
- package/dist/components/HeaderBar.js +13 -8
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/HistorySearchOverlay.js +4 -3
- package/dist/components/HistorySearchOverlay.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +8 -11
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/InvalidConfigDialog.js +2 -1
- package/dist/components/InvalidConfigDialog.js.map +2 -2
- package/dist/components/Logo.js +23 -67
- package/dist/components/Logo.js.map +2 -2
- package/dist/components/MCPServerApprovalDialog.js +2 -1
- package/dist/components/MCPServerApprovalDialog.js.map +2 -2
- package/dist/components/MCPServerDialogCopy.js +2 -1
- package/dist/components/MCPServerDialogCopy.js.map +2 -2
- package/dist/components/MCPServerMultiselectDialog.js +2 -1
- package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
- package/dist/components/MessageSelector.js +4 -3
- package/dist/components/MessageSelector.js.map +2 -2
- package/dist/components/ModeIndicator.js +2 -1
- package/dist/components/ModeIndicator.js.map +2 -2
- package/dist/components/ModelConfig.js +4 -3
- package/dist/components/ModelConfig.js.map +2 -2
- package/dist/components/ModelListManager.js +4 -3
- package/dist/components/ModelListManager.js.map +2 -2
- package/dist/components/ModelSelector/ModelSelector.js +26 -13
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/Onboarding.js +3 -2
- package/dist/components/Onboarding.js.map +2 -2
- package/dist/components/OperationSummary.js +130 -0
- package/dist/components/OperationSummary.js.map +7 -0
- package/dist/components/PromptInput.js +88 -75
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SensitiveFileWarning.js +31 -0
- package/dist/components/SensitiveFileWarning.js.map +7 -0
- package/dist/components/Spinner.js +71 -22
- package/dist/components/Spinner.js.map +2 -2
- package/dist/components/StructuredDiff.js +6 -8
- package/dist/components/StructuredDiff.js.map +2 -2
- package/dist/components/SubagentBlock.js +4 -2
- package/dist/components/SubagentBlock.js.map +2 -2
- package/dist/components/SubagentProgress.js +7 -4
- package/dist/components/SubagentProgress.js.map +2 -2
- package/dist/components/TaskCard.js +14 -11
- package/dist/components/TaskCard.js.map +2 -2
- package/dist/components/TextInput.js +9 -1
- package/dist/components/TextInput.js.map +2 -2
- package/dist/components/TodoPanel.js +44 -26
- package/dist/components/TodoPanel.js.map +2 -2
- package/dist/components/ToolUseLoader.js +2 -2
- package/dist/components/ToolUseLoader.js.map +2 -2
- package/dist/components/TreeConnector.js +4 -3
- package/dist/components/TreeConnector.js.map +2 -2
- package/dist/components/TrustDialog.js +2 -1
- package/dist/components/TrustDialog.js.map +2 -2
- package/dist/components/binary-feedback/BinaryFeedbackView.js +2 -1
- package/dist/components/binary-feedback/BinaryFeedbackView.js.map +2 -2
- package/dist/components/messages/AssistantTextMessage.js +17 -9
- package/dist/components/messages/AssistantTextMessage.js.map +2 -2
- package/dist/components/messages/AssistantToolUseMessage.js +8 -4
- package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
- package/dist/components/messages/GroupRenderer.js +2 -1
- package/dist/components/messages/GroupRenderer.js.map +2 -2
- package/dist/components/messages/NestedTasksPreview.js +13 -1
- package/dist/components/messages/NestedTasksPreview.js.map +2 -2
- package/dist/components/messages/ParallelTasksGroupView.js +4 -3
- package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
- package/dist/components/messages/TaskInModuleView.js +35 -15
- package/dist/components/messages/TaskInModuleView.js.map +2 -2
- package/dist/components/messages/TaskOutputContent.js +9 -6
- package/dist/components/messages/TaskOutputContent.js.map +2 -2
- package/dist/components/messages/UserPromptMessage.js +2 -2
- package/dist/components/messages/UserPromptMessage.js.map +2 -2
- package/dist/constants/colors.js +90 -72
- package/dist/constants/colors.js.map +2 -2
- package/dist/constants/toolInputExamples.js +84 -0
- package/dist/constants/toolInputExamples.js.map +7 -0
- package/dist/core/backupManager.js +321 -0
- package/dist/core/backupManager.js.map +7 -0
- package/dist/core/costTracker.js +9 -18
- package/dist/core/costTracker.js.map +2 -2
- package/dist/core/gitAutoCommit.js +287 -0
- package/dist/core/gitAutoCommit.js.map +7 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +2 -2
- package/dist/core/operationTracker.js +212 -0
- package/dist/core/operationTracker.js.map +7 -0
- package/dist/core/permissions/rules/allowedToolsRule.js +1 -1
- package/dist/core/permissions/rules/allowedToolsRule.js.map +2 -2
- package/dist/core/permissions/rules/autoEscalationRule.js +5 -0
- package/dist/core/permissions/rules/autoEscalationRule.js.map +2 -2
- package/dist/core/permissions/rules/projectBoundaryRule.js +5 -0
- package/dist/core/permissions/rules/projectBoundaryRule.js.map +2 -2
- package/dist/core/permissions/rules/sensitivePathsRule.js +5 -0
- package/dist/core/permissions/rules/sensitivePathsRule.js.map +2 -2
- package/dist/core/tokenStats.js +9 -0
- package/dist/core/tokenStats.js.map +7 -0
- package/dist/core/tokenStatsManager.js +331 -0
- package/dist/core/tokenStatsManager.js.map +7 -0
- package/dist/entrypoints/cli.js +115 -87
- package/dist/entrypoints/cli.js.map +2 -2
- package/dist/hooks/useAgentTokenStats.js +72 -0
- package/dist/hooks/useAgentTokenStats.js.map +7 -0
- package/dist/hooks/useAgentTranscripts.js +30 -6
- package/dist/hooks/useAgentTranscripts.js.map +2 -2
- package/dist/hooks/useLogMessages.js +12 -1
- package/dist/hooks/useLogMessages.js.map +2 -2
- package/dist/i18n/locales/en.js +6 -5
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +6 -5
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/permissions.js +28 -1
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +78 -4
- package/dist/query.js.map +3 -3
- package/dist/screens/REPL.js +23 -3
- package/dist/screens/REPL.js.map +2 -2
- package/dist/services/claude.js +54 -3
- package/dist/services/claude.js.map +2 -2
- package/dist/services/intelligentCompactor.js +1 -1
- package/dist/services/intelligentCompactor.js.map +2 -2
- package/dist/services/mcpClient.js +81 -25
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/sandbox/filesystemBoundary.js +58 -17
- package/dist/services/sandbox/filesystemBoundary.js.map +2 -2
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +3 -2
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +2 -1
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
- package/dist/tools/BashTool/BashTool.js +22 -3
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/BashTool/prompt.js +178 -34
- package/dist/tools/BashTool/prompt.js.map +2 -2
- package/dist/tools/FileEditTool/prompt.js +6 -3
- package/dist/tools/FileEditTool/prompt.js.map +2 -2
- package/dist/tools/FileWriteTool/prompt.js +4 -2
- package/dist/tools/FileWriteTool/prompt.js.map +2 -2
- package/dist/tools/MultiEditTool/prompt.js +5 -3
- package/dist/tools/MultiEditTool/prompt.js.map +2 -2
- package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -1
- package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js +3 -2
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js +3 -2
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/prompt.js +1 -1
- package/dist/tools/PlanModeTool/prompt.js.map +1 -1
- package/dist/tools/SkillTool/SkillTool.js +4 -3
- package/dist/tools/SkillTool/SkillTool.js.map +2 -2
- package/dist/tools/SkillTool/prompt.js +1 -1
- package/dist/tools/SkillTool/prompt.js.map +1 -1
- package/dist/tools/TaskOutputTool/TaskOutputTool.js +3 -2
- package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +8 -0
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/utils/CircuitBreaker.js +242 -0
- package/dist/utils/CircuitBreaker.js.map +7 -0
- package/dist/utils/ask.js +2 -0
- package/dist/utils/ask.js.map +2 -2
- package/dist/utils/config.js +47 -5
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/credentials/CredentialStore.js +1 -0
- package/dist/utils/credentials/CredentialStore.js.map +7 -0
- package/dist/utils/credentials/EncryptedFileStore.js +157 -0
- package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
- package/dist/utils/credentials/index.js +37 -0
- package/dist/utils/credentials/index.js.map +7 -0
- package/dist/utils/credentials/migration.js +82 -0
- package/dist/utils/credentials/migration.js.map +7 -0
- package/dist/utils/markdown.js +13 -1
- package/dist/utils/markdown.js.map +2 -2
- package/dist/utils/permissions/filesystem.js +5 -1
- package/dist/utils/permissions/filesystem.js.map +2 -2
- package/dist/utils/safePath.js +132 -0
- package/dist/utils/safePath.js.map +7 -0
- package/dist/utils/sensitiveFiles.js +125 -0
- package/dist/utils/sensitiveFiles.js.map +7 -0
- package/dist/utils/taskDisplayUtils.js +9 -9
- package/dist/utils/taskDisplayUtils.js.map +2 -2
- package/dist/utils/theme.js +6 -6
- package/dist/utils/theme.js.map +1 -1
- package/dist/utils/toolRiskClassification.js +207 -0
- package/dist/utils/toolRiskClassification.js.map +7 -0
- package/dist/utils/tooling/safeRender.js +5 -4
- package/dist/utils/tooling/safeRender.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +9 -7
- package/dist/hooks/useCancelRequest.js +0 -31
- package/dist/hooks/useCancelRequest.js.map +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/Spinner.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { useEffect, useRef, useState, useMemo } from 'react'\nimport { getTheme } from '@utils/theme'\nimport { sample } from 'lodash-es'\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport { formatNumber, formatTokenUsage } from '@utils/format'\n\n// NB: The third character in this string is an emoji that\n// renders on Windows consoles with a green background\nconst CHARACTERS =\n process.platform === 'darwin'\n ? ['\u00B7', '\u2722', '\u2733', '\u2217', '\u273B', '\u273D']\n : ['\u00B7', '\u2722', '*', '\u2217', '\u273B', '\u273D']\n\n// Streaming phases for better UX feedback\nexport type StreamingPhase =\n | 'thinking' // Initial thinking phase\n | 'generating' // Generating response content\n | 'tool_use' // Executing a tool\n | 'waiting' // Waiting for API response\n | 'deep_thinking' // Extended thinking mode (Claude thinking blocks)\n | 'retrying' // API retry in progress\n | 'permission' // Waiting for user permission\n | 'compacting' // Compacting conversation context\n | 'concurrent' // Running concurrent tasks\n\n// Store streaming state for display\nlet currentStreamingState: {\n phase: StreamingPhase\n toolName?: string\n tokenCount?: number\n chunkCount?: number\n // New fields for enhanced feedback\n retryCount?: number\n maxRetries?: number\n errorName?: string\n concurrentCount?: number\n thinkingMaxTokens?: number\n // Token usage from API (real-time streaming)\n inputTokens?: number\n outputTokens?: number\n // Character counts for approximate token estimation\n sentChars?: number // Characters sent to API (input)\n receivedChars?: number // Characters received from API (output)\n // Thinking timing\n thinkingStartTime?: number\n thinkingDurationMs?: number\n} = { phase: 'thinking' }\n\nexport function setStreamingState(\n state: Partial<typeof currentStreamingState>,\n): void {\n currentStreamingState = { ...currentStreamingState, ...state }\n}\n\nexport function resetStreamingState(): void {\n currentStreamingState = { phase: 'thinking' }\n}\n\nexport function getStreamingState(): typeof currentStreamingState {\n return currentStreamingState\n}\n\nconst MESSAGES = [\n 'Accomplishing',\n 'Actioning',\n 'Actualizing',\n 'Baking',\n 'Brewing',\n 'Calculating',\n 'Cerebrating',\n 'Churning',\n 'Coding',\n 'Coalescing',\n 'Cogitating',\n 'Computing',\n 'Conjuring',\n 'Considering',\n 'Cooking',\n 'Crafting',\n 'Creating',\n 'Crunching',\n 'Deliberating',\n 'Determining',\n 'Doing',\n 'Effecting',\n 'Finagling',\n 'Forging',\n 'Forming',\n 'Generating',\n 'Hatching',\n 'Herding',\n 'Honking',\n 'Hustling',\n 'Ideating',\n 'Inferring',\n 'Manifesting',\n 'Marinating',\n 'Moseying',\n 'Mulling',\n 'Mustering',\n 'Musing',\n 'Noodling',\n 'Percolating',\n 'Pondering',\n 'Processing',\n 'Puttering',\n 'Reticulating',\n 'Ruminating',\n 'Schlepping',\n 'Shucking',\n 'Simmering',\n 'Smooshing',\n 'Spinning',\n 'Stewing',\n 'Synthesizing',\n 'Thinking',\n 'Transmuting',\n 'Vibing',\n 'Working',\n]\n\ninterface SpinnerProps {\n /** Whether the spinner should be active (animating) */\n isActive?: boolean\n}\n\nexport function Spinner({ isActive = true }: SpinnerProps): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const message = useRef(sample(MESSAGES))\n const startTime = useRef(Date.now())\n const theme = getTheme()\n\n // Use unified animation manager instead of separate setInterval timers\n const { spinnerFrame, elapsedTime } = useUnifiedAnimation({\n enabled: isActive,\n startTime: startTime.current,\n spinnerFrameCount: frames.length,\n componentId: 'main-spinner',\n })\n\n // Get streaming state on each render (updates when spinnerFrame changes)\n const streamState = useMemo(() => getStreamingState(), [spinnerFrame])\n\n // Get phase-specific display\n const getPhaseDisplay = () => {\n switch (streamState.phase) {\n case 'deep_thinking': {\n // Show elapsed time and char count for thinking phase\n const elapsed = streamState.thinkingStartTime\n ? Math.floor((Date.now() - streamState.thinkingStartTime) / 1000)\n : 0\n const charInfo = streamState.tokenCount\n ? `${formatNumber(streamState.tokenCount)} chars`\n : ''\n\n if (streamState.thinkingMaxTokens && streamState.tokenCount) {\n return `Deep thinking (${elapsed}s \u00B7 ${charInfo} / ${formatNumber(streamState.thinkingMaxTokens)})`\n }\n return `Deep thinking (${elapsed}s${charInfo ? ' \u00B7 ' + charInfo : ''})`\n }\n\n case 'retrying':\n return streamState.retryCount && streamState.maxRetries\n ? `Retrying (${streamState.retryCount}/${streamState.maxRetries})${streamState.errorName ? ` \u00B7 ${streamState.errorName}` : ''}`\n : streamState.errorName\n ? `Retrying \u00B7 ${streamState.errorName}`\n : 'Retrying...'\n\n case 'permission':\n return 'Waiting for permission...'\n\n case 'compacting':\n return 'Compacting context...'\n\n case 'concurrent':\n return streamState.concurrentCount\n ? `Running ${streamState.concurrentCount} concurrent tasks...`\n : 'Running concurrent tasks...'\n\n case 'tool_use':\n return streamState.toolName\n ? `Using ${streamState.toolName}`\n : 'Executing tool'\n\n case 'generating':\n return 'Receiving'\n\n case 'waiting':\n return 'Waiting for response'\n\n case 'thinking':\n default:\n return message.current\n }\n }\n\n const phaseColors: Record<StreamingPhase, string> = {\n thinking: theme.minto,\n deep_thinking: theme.minto,\n generating: theme.success,\n tool_use: theme.warning,\n waiting: theme.secondaryText,\n retrying: theme.error,\n permission: theme.info,\n compacting: theme.info,\n concurrent: theme.success,\n }\n\n // Get token usage from streaming state (API values or approximate from chars)\n const getTokenDisplay = () => {\n // Prefer API-provided token counts\n if (streamState.inputTokens || streamState.outputTokens) {\n return formatTokenUsage(streamState.inputTokens, streamState.outputTokens)\n }\n // Fallback: approximate from received characters (~3 chars per token for mixed content)\n if (streamState.receivedChars && streamState.receivedChars > 0) {\n const approxTokens = Math.round(streamState.receivedChars / 3)\n return `\u2193 ~${formatNumber(approxTokens)}`\n }\n return ''\n }\n\n const tokenUsage = getTokenDisplay()\n\n // Get thinking duration if available\n const thinkingDuration = streamState.thinkingDurationMs\n ? Math.floor(streamState.thinkingDurationMs / 1000)\n : null\n\n return (\n <Box flexDirection=\"row\" marginTop={1}>\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={phaseColors[streamState.phase]}>\n {frames[spinnerFrame]}\n </Text>\n </Box>\n <Text color={phaseColors[streamState.phase]}>{getPhaseDisplay()}\u2026 </Text>\n <Text color={theme.secondaryText}>\n (<Text bold>esc</Text> to interrupt \u00B7 {elapsedTime}s\n {tokenUsage && <Text> \u00B7 {tokenUsage} tokens</Text>}\n {thinkingDuration !== null && thinkingDuration > 0 && (\n <Text> \u00B7 thought for {thinkingDuration}s</Text>\n )}\n )\n </Text>\n {getSessionState('currentError') && (\n <Text color={theme.secondaryText}>\n \u00B7 {getSessionState('currentError')}\n </Text>\n )}\n </Box>\n )\n}\n\ninterface SimpleSpinnerProps {\n /** Whether the spinner should be active (animating) */\n isActive?: boolean\n /** Optional label to display next to spinner */\n label?: string\n}\n\nexport function SimpleSpinner({\n isActive = true,\n label,\n}: SimpleSpinnerProps): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const startTime = useRef(Date.now())\n const theme = getTheme()\n\n // Use unified animation manager\n const { spinnerFrame } = useUnifiedAnimation({\n enabled: isActive,\n startTime: startTime.current,\n spinnerFrameCount: frames.length,\n componentId: 'simple-spinner',\n })\n\n return (\n <Box flexDirection=\"row\">\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={theme.minto}>{frames[spinnerFrame]}</Text>\n </Box>\n {label && <Text color={theme.secondaryText}> {label}</Text>}\n </Box>\n )\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAoB,QAAkB,eAAe;AACrD,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,uBAAwC;AACjD,SAAS,2BAA2B;AACpC,SAAS,cAAc,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { useEffect, useRef, useState, useMemo } from 'react'\nimport { getTheme } from '@utils/theme'\nimport { sample } from 'lodash-es'\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport { formatNumber, formatTokenUsage } from '@utils/format'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\n\n// NB: The third character in this string is an emoji that\n// renders on Windows consoles with a green background\nconst CHARACTERS =\n process.platform === 'darwin'\n ? ['\u00B7', '\u2722', '\u2733', '\u2217', '\u273B', '\u273D']\n : ['\u00B7', '\u2722', '*', '\u2217', '\u273B', '\u273D']\n\n// Streaming phases for better UX feedback\nexport type StreamingPhase =\n | 'thinking' // Initial thinking phase\n | 'generating' // Generating response content\n | 'tool_use' // Executing a tool\n | 'waiting' // Waiting for API response\n | 'deep_thinking' // Extended thinking mode (Claude thinking blocks)\n | 'retrying' // API retry in progress\n | 'permission' // Waiting for user permission\n | 'compacting' // Compacting conversation context\n | 'concurrent' // Running concurrent tasks\n\n// Streaming state type definition\nexport interface StreamingStateData {\n phase: StreamingPhase\n toolName?: string\n tokenCount?: number\n chunkCount?: number\n // New fields for enhanced feedback\n retryCount?: number\n maxRetries?: number\n errorName?: string\n concurrentCount?: number\n thinkingMaxTokens?: number\n // Token usage from API (real-time streaming)\n inputTokens?: number\n outputTokens?: number\n // Character counts for approximate token estimation\n sentChars?: number // Characters sent to API (input)\n receivedChars?: number // Characters received from API (output)\n // Thinking timing\n thinkingStartTime?: number\n thinkingDurationMs?: number\n}\n\n// Default state factory\nfunction createDefaultState(): StreamingStateData {\n return { phase: 'thinking' }\n}\n\n// Store streaming state per agent (agentId -> state)\n// '__main__' is used for the main/root agent\nconst streamingStates: Map<string, StreamingStateData> = new Map([\n ['__main__', createDefaultState()],\n])\n\n// Current active agent context (stack to support nesting)\nconst agentContextStack: string[] = ['__main__']\n\n/**\n * Get the current active agent ID\n */\nexport function getCurrentAgentContext(): string {\n return agentContextStack[agentContextStack.length - 1] ?? '__main__'\n}\n\n/**\n * Push a new agent context (when entering a subagent)\n */\nexport function pushAgentContext(agentId: string): void {\n agentContextStack.push(agentId)\n if (!streamingStates.has(agentId)) {\n streamingStates.set(agentId, createDefaultState())\n }\n}\n\n/**\n * Pop the current agent context (when exiting a subagent)\n */\nexport function popAgentContext(): string | undefined {\n if (agentContextStack.length > 1) {\n return agentContextStack.pop()\n }\n return undefined\n}\n\n/**\n * Set streaming state for a specific agent (or current context if not specified)\n */\nexport function setStreamingState(\n state: Partial<StreamingStateData>,\n agentId?: string,\n): void {\n const targetId = agentId ?? getCurrentAgentContext()\n const current = streamingStates.get(targetId) ?? createDefaultState()\n streamingStates.set(targetId, { ...current, ...state })\n}\n\n/**\n * Reset streaming state for a specific agent (or current context if not specified)\n */\nexport function resetStreamingState(agentId?: string): void {\n const targetId = agentId ?? getCurrentAgentContext()\n streamingStates.set(targetId, createDefaultState())\n}\n\n/**\n * Get streaming state for a specific agent (or current context if not specified)\n */\nexport function getStreamingState(agentId?: string): StreamingStateData {\n const targetId = agentId ?? getCurrentAgentContext()\n return streamingStates.get(targetId) ?? createDefaultState()\n}\n\n/**\n * Get streaming state for the main/root agent (used by main Spinner)\n */\nexport function getMainStreamingState(): StreamingStateData {\n return streamingStates.get('__main__') ?? createDefaultState()\n}\n\n/**\n * Clean up streaming state for a completed agent\n */\nexport function cleanupAgentStreamingState(agentId: string): void {\n streamingStates.delete(agentId)\n}\n\nconst MESSAGES = [\n 'Accomplishing',\n 'Actioning',\n 'Actualizing',\n 'Baking',\n 'Brewing',\n 'Calculating',\n 'Cerebrating',\n 'Churning',\n 'Coding',\n 'Coalescing',\n 'Cogitating',\n 'Computing',\n 'Conjuring',\n 'Considering',\n 'Cooking',\n 'Crafting',\n 'Creating',\n 'Crunching',\n 'Deliberating',\n 'Determining',\n 'Doing',\n 'Effecting',\n 'Finagling',\n 'Forging',\n 'Forming',\n 'Generating',\n 'Hatching',\n 'Herding',\n 'Honking',\n 'Hustling',\n 'Ideating',\n 'Inferring',\n 'Manifesting',\n 'Marinating',\n 'Moseying',\n 'Mulling',\n 'Mustering',\n 'Musing',\n 'Noodling',\n 'Percolating',\n 'Pondering',\n 'Processing',\n 'Puttering',\n 'Reticulating',\n 'Ruminating',\n 'Schlepping',\n 'Shucking',\n 'Simmering',\n 'Smooshing',\n 'Spinning',\n 'Stewing',\n 'Synthesizing',\n 'Thinking',\n 'Transmuting',\n 'Vibing',\n 'Working',\n]\n\ninterface SpinnerProps {\n /** Whether the spinner should be active (animating) */\n isActive?: boolean\n}\n\nexport function Spinner({ isActive = true }: SpinnerProps): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const message = useRef(sample(MESSAGES))\n const startTime = useRef(Date.now())\n const theme = getTheme()\n\n // Use unified animation manager instead of separate setInterval timers\n const { spinnerFrame, elapsedTime } = useUnifiedAnimation({\n enabled: isActive,\n startTime: startTime.current,\n spinnerFrameCount: frames.length,\n componentId: 'main-spinner',\n })\n\n // Get main streaming state on each render (updates when spinnerFrame changes)\n // Use getMainStreamingState to avoid subagent interference\n const streamState = useMemo(() => getMainStreamingState(), [spinnerFrame])\n\n // Get phase-specific display\n const getPhaseDisplay = () => {\n switch (streamState.phase) {\n case 'deep_thinking': {\n // Show elapsed time and char count for thinking phase\n const elapsed = streamState.thinkingStartTime\n ? Math.floor((Date.now() - streamState.thinkingStartTime) / 1000)\n : 0\n const charInfo = streamState.tokenCount\n ? `${formatNumber(streamState.tokenCount)} chars`\n : ''\n\n if (streamState.thinkingMaxTokens && streamState.tokenCount) {\n return `Deep thinking (${elapsed}s \u00B7 ${charInfo} / ${formatNumber(streamState.thinkingMaxTokens)})`\n }\n return `Deep thinking (${elapsed}s${charInfo ? ' \u00B7 ' + charInfo : ''})`\n }\n\n case 'retrying':\n return streamState.retryCount && streamState.maxRetries\n ? `Retrying (${streamState.retryCount}/${streamState.maxRetries})${streamState.errorName ? ` \u00B7 ${streamState.errorName}` : ''}`\n : streamState.errorName\n ? `Retrying \u00B7 ${streamState.errorName}`\n : 'Retrying...'\n\n case 'permission':\n return 'Waiting for permission...'\n\n case 'compacting':\n return 'Compacting context...'\n\n case 'concurrent':\n return streamState.concurrentCount\n ? `Running ${streamState.concurrentCount} concurrent tasks...`\n : 'Running concurrent tasks...'\n\n case 'tool_use':\n return streamState.toolName\n ? `Using ${streamState.toolName}`\n : 'Executing tool'\n\n case 'generating':\n return 'Receiving'\n\n case 'waiting':\n return 'Waiting for response'\n\n case 'thinking':\n default:\n return message.current\n }\n }\n\n // \u4F7F\u7528\u8BED\u4E49\u989C\u8272\u7CFB\u7EDF\uFF0C\u54C1\u724C\u8272\u7528\u4E8E\u601D\u8003\uFF0C\u72B6\u6001\u8272\u7528\u4E8E\u5404\u9636\u6BB5\n const phaseColors: Record<StreamingPhase, string> = {\n thinking: BRAND_GRADIENT.MIDDLE, // \u7C89\u7D2B - \u601D\u8003\u4E2D\n deep_thinking: BRAND_GRADIENT.START, // \u7D2B\u84DD - \u6DF1\u5EA6\u601D\u8003\n generating: SEMANTIC_COLORS.success, // \u7EFF\u8272 - \u751F\u6210\u4E2D\n tool_use: SEMANTIC_COLORS.running, // \u7C89\u7D2B - \u5DE5\u5177\u6267\u884C\n waiting: SEMANTIC_COLORS.dim, // \u7070\u8272 - \u7B49\u5F85\n retrying: SEMANTIC_COLORS.error, // \u7EA2\u8272 - \u91CD\u8BD5\n permission: SEMANTIC_COLORS.info, // \u84DD\u8272 - \u6743\u9650\n compacting: SEMANTIC_COLORS.info, // \u84DD\u8272 - \u538B\u7F29\n concurrent: SEMANTIC_COLORS.success, // \u7EFF\u8272 - \u5E76\u53D1\n }\n\n // Get token usage from streaming state (API values or approximate from chars)\n const getTokenDisplay = () => {\n // Prefer API-provided token counts - always show bidirectional if available\n if (streamState.inputTokens || streamState.outputTokens) {\n return formatTokenUsage(streamState.inputTokens, streamState.outputTokens)\n }\n // Fallback: approximate from received characters (~3 chars per token for mixed content)\n if (streamState.receivedChars && streamState.receivedChars > 0) {\n const approxOutputTokens = Math.round(streamState.receivedChars / 3)\n // If we have sentChars, show both directions\n if (streamState.sentChars && streamState.sentChars > 0) {\n const approxInputTokens = Math.round(streamState.sentChars / 3)\n return `\u2191 ~${formatNumber(approxInputTokens)} \u00B7 \u2193 ~${formatNumber(approxOutputTokens)}`\n }\n return `\u2193 ~${formatNumber(approxOutputTokens)}`\n }\n return ''\n }\n\n const tokenUsage = getTokenDisplay()\n\n // Get thinking duration if available\n const thinkingDuration = streamState.thinkingDurationMs\n ? Math.floor(streamState.thinkingDurationMs / 1000)\n : null\n\n return (\n <Box flexDirection=\"row\" marginTop={1}>\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={phaseColors[streamState.phase]}>\n {frames[spinnerFrame]}\n </Text>\n </Box>\n <Text color={phaseColors[streamState.phase]}>{getPhaseDisplay()}\u2026 </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n (<Text bold>esc esc</Text> to cancel \u00B7 {elapsedTime}s\n {tokenUsage && <Text> \u00B7 {tokenUsage}</Text>}\n {thinkingDuration !== null && thinkingDuration > 0 && (\n <Text> \u00B7 thinking {thinkingDuration}s</Text>\n )}\n )\n </Text>\n {getSessionState('currentError') && (\n <Text color={SEMANTIC_COLORS.dim}>\n \u00B7 {getSessionState('currentError')}\n </Text>\n )}\n </Box>\n )\n}\n\ninterface SimpleSpinnerProps {\n /** Whether the spinner should be active (animating) */\n isActive?: boolean\n /** Optional label to display next to spinner */\n label?: string\n}\n\nexport function SimpleSpinner({\n isActive = true,\n label,\n}: SimpleSpinnerProps): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const startTime = useRef(Date.now())\n\n // Use unified animation manager\n const { spinnerFrame } = useUnifiedAnimation({\n enabled: isActive,\n startTime: startTime.current,\n spinnerFrameCount: frames.length,\n componentId: 'simple-spinner',\n })\n\n return (\n <Box flexDirection=\"row\">\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={BRAND_GRADIENT.MIDDLE}>{frames[spinnerFrame]}</Text>\n </Box>\n {label && <Text color={SEMANTIC_COLORS.dim}> {label}</Text>}\n </Box>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { getTheme } from "../utils/theme.js";
|
|
4
4
|
import { useMemo } from "react";
|
|
5
5
|
import { wrapText } from "../utils/format.js";
|
|
6
|
+
import { SEMANTIC_COLORS } from "../constants/colors.js";
|
|
6
7
|
function StructuredDiff({
|
|
7
8
|
patch,
|
|
8
9
|
dim,
|
|
@@ -54,9 +55,8 @@ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
|
|
|
54
55
|
), /* @__PURE__ */ React.createElement(
|
|
55
56
|
Text,
|
|
56
57
|
{
|
|
57
|
-
color: overrideTheme ? theme.text : void 0,
|
|
58
|
-
backgroundColor: dim ? theme.diff.addedDimmed : theme.diff.added
|
|
59
|
-
dimColor: dim
|
|
58
|
+
color: dim ? SEMANTIC_COLORS.dim : overrideTheme ? theme.text : void 0,
|
|
59
|
+
backgroundColor: dim ? theme.diff.addedDimmed : theme.diff.added
|
|
60
60
|
},
|
|
61
61
|
line
|
|
62
62
|
)));
|
|
@@ -70,9 +70,8 @@ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
|
|
|
70
70
|
), /* @__PURE__ */ React.createElement(
|
|
71
71
|
Text,
|
|
72
72
|
{
|
|
73
|
-
color: overrideTheme ? theme.text : void 0,
|
|
74
|
-
backgroundColor: dim ? theme.diff.removedDimmed : theme.diff.removed
|
|
75
|
-
dimColor: dim
|
|
73
|
+
color: dim ? SEMANTIC_COLORS.dim : overrideTheme ? theme.text : void 0,
|
|
74
|
+
backgroundColor: dim ? theme.diff.removedDimmed : theme.diff.removed
|
|
76
75
|
},
|
|
77
76
|
line
|
|
78
77
|
)));
|
|
@@ -86,8 +85,7 @@ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
|
|
|
86
85
|
), /* @__PURE__ */ React.createElement(
|
|
87
86
|
Text,
|
|
88
87
|
{
|
|
89
|
-
color: overrideTheme ? theme.text : void 0
|
|
90
|
-
dimColor: dim
|
|
88
|
+
color: dim ? SEMANTIC_COLORS.dim : overrideTheme ? theme.text : void 0
|
|
91
89
|
},
|
|
92
90
|
line
|
|
93
91
|
)));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/StructuredDiff.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { Hunk } from 'diff'\nimport { getTheme, ThemeNames } from '@utils/theme'\nimport { useMemo } from 'react'\nimport { wrapText } from '@utils/format'\n\ntype Props = {\n patch: Hunk\n dim: boolean\n width: number\n overrideTheme?: ThemeNames // custom theme for previews\n key?: React.Key\n}\n\nexport function StructuredDiff({\n patch,\n dim,\n width,\n overrideTheme,\n}: Props): React.ReactNode {\n const diff = useMemo(\n () => formatDiff(patch.lines, patch.oldStart, width, dim, overrideTheme),\n [patch.lines, patch.oldStart, width, dim, overrideTheme],\n )\n\n return diff.map((_, i) => <Box key={i}>{_}</Box>)\n}\n\nfunction formatDiff(\n lines: string[],\n startingLineNumber: number,\n width: number,\n dim: boolean,\n overrideTheme?: ThemeNames,\n): React.ReactNode[] {\n const theme = getTheme(overrideTheme)\n\n const ls = numberDiffLines(\n lines.map(code => {\n if (code.startsWith('+')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'add',\n }\n }\n if (code.startsWith('-')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'remove',\n }\n }\n return { code, i: 0, type: 'nochange' }\n }),\n startingLineNumber,\n )\n\n const maxLineNumber = Math.max(...ls.map(({ i }) => i))\n const maxWidth = maxLineNumber.toString().length\n\n return ls.flatMap(({ type, code, i }) => {\n const wrappedLines = wrapText(code, width - maxWidth)\n return wrappedLines.map((line, lineIndex) => {\n const key = `${type}-${i}-${lineIndex}`\n switch (type) {\n case 'add':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={overrideTheme
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AAEvB,SAAS,gBAA4B;AACrC,SAAS,eAAe;AACxB,SAAS,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { Hunk } from 'diff'\nimport { getTheme, ThemeNames } from '@utils/theme'\nimport { useMemo } from 'react'\nimport { wrapText } from '@utils/format'\nimport { SEMANTIC_COLORS } from '@constants/colors'\n\ntype Props = {\n patch: Hunk\n dim: boolean\n width: number\n overrideTheme?: ThemeNames // custom theme for previews\n key?: React.Key\n}\n\nexport function StructuredDiff({\n patch,\n dim,\n width,\n overrideTheme,\n}: Props): React.ReactNode {\n const diff = useMemo(\n () => formatDiff(patch.lines, patch.oldStart, width, dim, overrideTheme),\n [patch.lines, patch.oldStart, width, dim, overrideTheme],\n )\n\n return diff.map((_, i) => <Box key={i}>{_}</Box>)\n}\n\nfunction formatDiff(\n lines: string[],\n startingLineNumber: number,\n width: number,\n dim: boolean,\n overrideTheme?: ThemeNames,\n): React.ReactNode[] {\n const theme = getTheme(overrideTheme)\n\n const ls = numberDiffLines(\n lines.map(code => {\n if (code.startsWith('+')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'add',\n }\n }\n if (code.startsWith('-')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'remove',\n }\n }\n return { code, i: 0, type: 'nochange' }\n }),\n startingLineNumber,\n )\n\n const maxLineNumber = Math.max(...ls.map(({ i }) => i))\n const maxWidth = maxLineNumber.toString().length\n\n return ls.flatMap(({ type, code, i }) => {\n const wrappedLines = wrapText(code, width - maxWidth)\n return wrappedLines.map((line, lineIndex) => {\n const key = `${type}-${i}-${lineIndex}`\n switch (type) {\n case 'add':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={\n dim\n ? SEMANTIC_COLORS.dim\n : overrideTheme\n ? theme.text\n : undefined\n }\n backgroundColor={\n dim ? theme.diff.addedDimmed : theme.diff.added\n }\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n case 'remove':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={\n dim\n ? SEMANTIC_COLORS.dim\n : overrideTheme\n ? theme.text\n : undefined\n }\n backgroundColor={\n dim ? theme.diff.removedDimmed : theme.diff.removed\n }\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n case 'nochange':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={\n dim\n ? SEMANTIC_COLORS.dim\n : overrideTheme\n ? theme.text\n : undefined\n }\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n }\n })\n })\n}\n\nfunction LineNumber({\n i,\n width,\n}: {\n i: number | undefined\n width: number\n}): React.ReactNode {\n return (\n <Text color={getTheme().secondaryText}>\n {i !== undefined ? i.toString().padStart(width) : ' '.repeat(width)}{' '}\n </Text>\n )\n}\n\nfunction numberDiffLines(\n diff: { code: string; type: string }[],\n startLine: number,\n): { code: string; type: string; i: number }[] {\n let i = startLine\n const result: { code: string; type: string; i: number }[] = []\n const queue = [...diff]\n\n while (queue.length > 0) {\n const { code, type } = queue.shift()!\n const line = {\n code: code,\n type,\n i,\n }\n\n // Update counters based on change type\n switch (type) {\n case 'nochange':\n i++\n result.push(line)\n break\n case 'add':\n i++\n result.push(line)\n break\n case 'remove': {\n result.push(line)\n let numRemoved = 0\n while (queue[0]?.type === 'remove') {\n i++\n const { code, type } = queue.shift()!\n const line = {\n code: code,\n type,\n i,\n }\n result.push(line)\n numRemoved++\n }\n i -= numRemoved\n break\n }\n }\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["code", "type", "line"]
|
|
7
7
|
}
|
|
@@ -5,6 +5,7 @@ import { getTodoStatusSymbol } from "../constants/symbols.js";
|
|
|
5
5
|
import { AgentThinkingBlock } from "./AgentThinkingBlock.js";
|
|
6
6
|
import { ToolExecutionBlock } from "./ToolExecutionBlock.js";
|
|
7
7
|
import { AgentResponseBlock } from "./AgentResponseBlock.js";
|
|
8
|
+
import { useAgentTokenStats, formatTokenCount } from "../hooks/useAgentTokenStats.js";
|
|
8
9
|
function SubagentBlock({
|
|
9
10
|
subagent,
|
|
10
11
|
indent = 0,
|
|
@@ -13,6 +14,7 @@ function SubagentBlock({
|
|
|
13
14
|
const theme = getTheme();
|
|
14
15
|
const [expanded, setExpanded] = useState(isExpanded);
|
|
15
16
|
const marginLeft = indent * 2;
|
|
17
|
+
const tokenStats = useAgentTokenStats(subagent.id);
|
|
16
18
|
const todos = extractSubagentTodos(subagent);
|
|
17
19
|
const thinking = extractThinkingMessage(subagent);
|
|
18
20
|
const toolExecutions = extractToolExecutions(subagent);
|
|
@@ -20,7 +22,7 @@ function SubagentBlock({
|
|
|
20
22
|
const duration = subagent.metrics.endTime ? ((subagent.metrics.endTime - subagent.metrics.startTime) / 1e3).toFixed(
|
|
21
23
|
1
|
|
22
24
|
) : "...";
|
|
23
|
-
const tokenCount =
|
|
25
|
+
const tokenCount = tokenStats?.grandTotalTokens ?? 0;
|
|
24
26
|
const statusIndicator = getStatusIndicator(subagent.status);
|
|
25
27
|
const statusColor = getStatusColor(subagent.status, theme);
|
|
26
28
|
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft, marginTop: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.brand }, "\u{1F916} "), /* @__PURE__ */ React.createElement(Text, null, "Launching subagent: ", /* @__PURE__ */ React.createElement(Text, { bold: true }, subagent.agentType))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u251C\u2500 "), /* @__PURE__ */ React.createElement(Text, null, "Task: ", subagent.taskName)), expanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502"), todos && todos.length > 0 && /* @__PURE__ */ React.createElement(SubagentTodosSection, { todos, theme }), thinking && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502 "), /* @__PURE__ */ React.createElement(
|
|
@@ -38,7 +40,7 @@ function SubagentBlock({
|
|
|
38
40
|
indent: 0,
|
|
39
41
|
isSubagent: true
|
|
40
42
|
}
|
|
41
|
-
)), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusIndicator, " ", getStatusLabel(subagent.status)), subagent.status === "completed" && /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " ", "(", duration, "s, ", tokenCount, " tokens)")), !expanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Ctrl+O"), " to expand details")));
|
|
43
|
+
)), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusIndicator, " ", getStatusLabel(subagent.status)), subagent.status === "completed" && /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " ", "(", duration, "s, ", formatTokenCount(tokenCount), " tokens)")), !expanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Ctrl+O"), " to expand details")));
|
|
42
44
|
}
|
|
43
45
|
function SubagentTodosSection({
|
|
44
46
|
todos,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/SubagentBlock.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * SubagentBlock - Embedded subagent execution display\n *\n * Renders a complete subagent execution hierarchically with:\n * - Header (task name, agent type, status)\n * - Optional: Subtasks (if subagent has todos)\n * - Optional: Thinking phase\n * - Optional: Tool executions\n * - Optional: Response\n * - Footer (completion status)\n *\n * Supports collapsible internal content\n */\n\nimport React, { useState } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { SYMBOLS, getTodoStatusSymbol } from '@constants/symbols'\nimport { AgentThinkingBlock } from './AgentThinkingBlock'\nimport { ToolExecutionBlock } from './ToolExecutionBlock'\nimport { AgentResponseBlock } from './AgentResponseBlock'\nimport type { SubagentState } from '@minto-types/subagent'\nimport type { TodoItem } from '@utils/todoStorage'\nimport type { AssistantMessage } from '@minto-types/conversation'\nimport type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\n\ninterface Props {\n subagent: SubagentState\n indent?: number\n isExpanded?: boolean\n}\n\n/**\n * SubagentBlock - Hierarchical embedded subagent display\n *\n * Visual format (indent=1):\n * ```\n * \uD83E\uDD16 Launching subagent: backend-architect\n * \u251C\u2500 Task: Design authentication system\n * \u2502\n * \u2502 \uD83E\uDD14 Analyzing requirements... (300ms)\n * \u2502\n * \u2502 \uD83D\uDD27 Tool: Read package.json\n * \u2502 \u2514\u2500 \u2713 Read 50 lines (80ms)\n * \u2502\n * \u2502 \uD83D\uDCAC I recommend using JWT...\n * \u2502\n * \u2514\u2500 \u2713 Completed (2.5s, 1200 tokens)\n * ```\n */\nexport function SubagentBlock({\n subagent,\n indent = 0,\n isExpanded = false,\n}: Props): React.ReactNode {\n const theme = getTheme()\n const [expanded, setExpanded] = useState(isExpanded)\n const marginLeft = indent * 2\n\n // Extract todos if present (from subagent execution)\n const todos = extractSubagentTodos(subagent)\n const thinking = extractThinkingMessage(subagent)\n const toolExecutions = extractToolExecutions(subagent)\n const response = extractResponse(subagent)\n\n // Calculate metrics\n const duration = subagent.metrics.endTime\n ? ((subagent.metrics.endTime - subagent.metrics.startTime) / 1000).toFixed(\n 1,\n )\n : '...'\n const tokenCount = subagent.metrics.tokenCount || 0\n\n // Status indicator\n const statusIndicator = getStatusIndicator(subagent.status)\n const statusColor = getStatusColor(subagent.status, theme)\n\n return (\n <Box flexDirection=\"column\" marginLeft={marginLeft} marginTop={1}>\n {/* Subagent Header */}\n <Box flexDirection=\"row\">\n <Text color={theme.brand}>\uD83E\uDD16 </Text>\n <Text>\n Launching subagent: <Text bold>{subagent.agentType}</Text>\n </Text>\n </Box>\n\n {/* Task description */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u251C\u2500 </Text>\n <Text>Task: {subagent.taskName}</Text>\n </Box>\n\n {/* Collapsible internal content */}\n {expanded && (\n <Box flexDirection=\"column\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2502</Text>\n\n {/* Subagent subtasks (if present) */}\n {todos && todos.length > 0 && (\n <SubagentTodosSection todos={todos} theme={theme} />\n )}\n\n {/* Subagent thinking */}\n {thinking && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentThinkingBlock\n message={thinking.message}\n startTime={thinking.startTime}\n showSpinner={subagent.status === 'running'}\n showElapsedTime={false}\n />\n </Box>\n )}\n\n {/* Subagent tool executions */}\n {toolExecutions.map((tool, idx) => (\n <Box key={idx} flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <ToolExecutionBlock tool={tool} indent={0} />\n </Box>\n ))}\n\n {/* Subagent response */}\n {response && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentResponseBlock\n message={response}\n indent={0}\n isSubagent={true}\n />\n </Box>\n )}\n\n <Text color={theme.dimmedText}>\u2502</Text>\n </Box>\n )}\n\n {/* Subagent Footer (completion status) */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2514\u2500 </Text>\n <Text color={statusColor}>\n {statusIndicator} {getStatusLabel(subagent.status)}\n </Text>\n {subagent.status === 'completed' && (\n <Text color={theme.mutedText}>\n {' '}\n ({duration}s, {tokenCount} tokens)\n </Text>\n )}\n </Box>\n\n {/* Expand/collapse hint (only when collapsed) */}\n {!expanded && (\n <Box flexDirection=\"row\" marginLeft={4}>\n <Text color={theme.mutedText}>\n Press <Text bold>Ctrl+O</Text> to expand details\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * SubagentTodosSection - Renders subagent's subtasks\n */\nfunction SubagentTodosSection({\n todos,\n theme,\n}: {\n todos: TodoItem[]\n theme: ReturnType<typeof getTheme>\n}) {\n return (\n <Box flexDirection=\"column\" marginLeft={1} marginY={1}>\n <Box flexDirection=\"row\">\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text color={theme.dimmedText}>Subtasks:</Text>\n </Box>\n {todos.map((todo, idx) => (\n <Box key={idx} flexDirection=\"row\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text>{getTodoStatusSymbol(todo.status)} </Text>\n <Text\n color={todo.status === 'completed' ? theme.dim : undefined}\n strikethrough={todo.status === 'completed'}\n >\n {todo.content}\n </Text>\n </Box>\n ))}\n </Box>\n )\n}\n\n/**\n * Helper: Extract todos from subagent messages\n * (This would be populated from TodoWriteTool calls within the subagent)\n */\nfunction extractSubagentTodos(subagent: SubagentState): TodoItem[] | null {\n // TODO: Implement extraction of todos from subagent messages\n // For now, return null (will be implemented in Phase 4)\n return null\n}\n\n/**\n * Helper: Extract thinking message from subagent\n */\nfunction extractThinkingMessage(\n subagent: SubagentState,\n): { message: string; startTime: number } | null {\n // Find thinking blocks in messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const thinkingBlock = msg.message.content.find(\n (block: any) => block.type === 'thinking',\n )\n if (thinkingBlock && 'thinking' in thinkingBlock) {\n return {\n message: thinkingBlock.thinking || 'Thinking...',\n startTime: subagent.metrics.startTime,\n }\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Extract tool executions from subagent\n */\nfunction extractToolExecutions(subagent: SubagentState): Array<{\n id: string\n name: string\n input: any\n status: 'pending' | 'running' | 'completed' | 'error'\n startTime: number\n durationMs?: number\n}> {\n const tools: Array<any> = []\n\n // Extract tool_use blocks from messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const toolBlocks = msg.message.content.filter(\n (block: any) => block.type === 'tool_use',\n )\n for (const toolBlock of toolBlocks) {\n if ('name' in toolBlock && 'id' in toolBlock) {\n tools.push({\n id: toolBlock.id,\n name: toolBlock.name,\n input: toolBlock.input,\n status: 'completed', // Simplified for now\n startTime: subagent.metrics.startTime,\n durationMs: 100, // Placeholder\n })\n }\n }\n }\n }\n\n return tools\n}\n\n/**\n * Helper: Extract response message from subagent\n */\nfunction extractResponse(subagent: SubagentState): AssistantMessage | null {\n // Find last assistant message with text content\n for (let i = subagent.messages.length - 1; i >= 0; i--) {\n const msg = subagent.messages[i]\n if (msg.type === 'assistant' && 'message' in msg) {\n const hasText = msg.message.content.some(\n (block: any) => block.type === 'text' && block.text.trim(),\n )\n if (hasText) {\n return msg as AssistantMessage\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Get status indicator symbol\n */\nfunction getStatusIndicator(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return '\u25CB'\n case 'queued':\n return '\u2139'\n case 'running':\n return '\u22EF'\n case 'completed':\n return '\u2713'\n case 'error':\n return '\u2716'\n }\n}\n\n/**\n * Helper: Get status color\n */\nfunction getStatusColor(\n status: SubagentState['status'],\n theme: ReturnType<typeof getTheme>,\n): string {\n switch (status) {\n case 'completed':\n return theme.success\n case 'error':\n return theme.error\n case 'running':\n return theme.brand\n default:\n return theme.dimmedText\n }\n}\n\n/**\n * Helper: Get status label\n */\nfunction getStatusLabel(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return 'Initializing...'\n case 'queued':\n return 'Queued'\n case 'running':\n return 'Running...'\n case 'completed':\n return 'Done'\n case 'error':\n return 'Error'\n }\n}\n"],
|
|
5
|
-
"mappings": "AAcA,OAAO,SAAS,gBAAgB;AAChC,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB;AACzB,SAAkB,2BAA2B;AAC7C,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;
|
|
4
|
+
"sourcesContent": ["/**\n * SubagentBlock - Embedded subagent execution display\n *\n * Renders a complete subagent execution hierarchically with:\n * - Header (task name, agent type, status)\n * - Optional: Subtasks (if subagent has todos)\n * - Optional: Thinking phase\n * - Optional: Tool executions\n * - Optional: Response\n * - Footer (completion status)\n *\n * Supports collapsible internal content\n */\n\nimport React, { useState } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { SYMBOLS, getTodoStatusSymbol } from '@constants/symbols'\nimport { AgentThinkingBlock } from './AgentThinkingBlock'\nimport { ToolExecutionBlock } from './ToolExecutionBlock'\nimport { AgentResponseBlock } from './AgentResponseBlock'\nimport { useAgentTokenStats, formatTokenCount } from '@hooks/useAgentTokenStats'\nimport type { SubagentState } from '@minto-types/subagent'\nimport type { TodoItem } from '@utils/todoStorage'\nimport type { AssistantMessage } from '@minto-types/conversation'\nimport type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\n\ninterface Props {\n subagent: SubagentState\n indent?: number\n isExpanded?: boolean\n}\n\n/**\n * SubagentBlock - Hierarchical embedded subagent display\n *\n * Visual format (indent=1):\n * ```\n * \uD83E\uDD16 Launching subagent: backend-architect\n * \u251C\u2500 Task: Design authentication system\n * \u2502\n * \u2502 \uD83E\uDD14 Analyzing requirements... (300ms)\n * \u2502\n * \u2502 \uD83D\uDD27 Tool: Read package.json\n * \u2502 \u2514\u2500 \u2713 Read 50 lines (80ms)\n * \u2502\n * \u2502 \uD83D\uDCAC I recommend using JWT...\n * \u2502\n * \u2514\u2500 \u2713 Completed (2.5s, 1200 tokens)\n * ```\n */\nexport function SubagentBlock({\n subagent,\n indent = 0,\n isExpanded = false,\n}: Props): React.ReactNode {\n const theme = getTheme()\n const [expanded, setExpanded] = useState(isExpanded)\n const marginLeft = indent * 2\n\n // Get real-time token stats from unified TokenStatsManager\n const tokenStats = useAgentTokenStats(subagent.id)\n\n // Extract todos if present (from subagent execution)\n const todos = extractSubagentTodos(subagent)\n const thinking = extractThinkingMessage(subagent)\n const toolExecutions = extractToolExecutions(subagent)\n const response = extractResponse(subagent)\n\n // Calculate metrics\n const duration = subagent.metrics.endTime\n ? ((subagent.metrics.endTime - subagent.metrics.startTime) / 1000).toFixed(\n 1,\n )\n : '...'\n // Use token stats from TokenStatsManager (single source of truth)\n const tokenCount = tokenStats?.grandTotalTokens ?? 0\n\n // Status indicator\n const statusIndicator = getStatusIndicator(subagent.status)\n const statusColor = getStatusColor(subagent.status, theme)\n\n return (\n <Box flexDirection=\"column\" marginLeft={marginLeft} marginTop={1}>\n {/* Subagent Header */}\n <Box flexDirection=\"row\">\n <Text color={theme.brand}>\uD83E\uDD16 </Text>\n <Text>\n Launching subagent: <Text bold>{subagent.agentType}</Text>\n </Text>\n </Box>\n\n {/* Task description */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u251C\u2500 </Text>\n <Text>Task: {subagent.taskName}</Text>\n </Box>\n\n {/* Collapsible internal content */}\n {expanded && (\n <Box flexDirection=\"column\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2502</Text>\n\n {/* Subagent subtasks (if present) */}\n {todos && todos.length > 0 && (\n <SubagentTodosSection todos={todos} theme={theme} />\n )}\n\n {/* Subagent thinking */}\n {thinking && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentThinkingBlock\n message={thinking.message}\n startTime={thinking.startTime}\n showSpinner={subagent.status === 'running'}\n showElapsedTime={false}\n />\n </Box>\n )}\n\n {/* Subagent tool executions */}\n {toolExecutions.map((tool, idx) => (\n <Box key={idx} flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <ToolExecutionBlock tool={tool} indent={0} />\n </Box>\n ))}\n\n {/* Subagent response */}\n {response && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentResponseBlock\n message={response}\n indent={0}\n isSubagent={true}\n />\n </Box>\n )}\n\n <Text color={theme.dimmedText}>\u2502</Text>\n </Box>\n )}\n\n {/* Subagent Footer (completion status) */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2514\u2500 </Text>\n <Text color={statusColor}>\n {statusIndicator} {getStatusLabel(subagent.status)}\n </Text>\n {subagent.status === 'completed' && (\n <Text color={theme.mutedText}>\n {' '}\n ({duration}s, {formatTokenCount(tokenCount)} tokens)\n </Text>\n )}\n </Box>\n\n {/* Expand/collapse hint (only when collapsed) */}\n {!expanded && (\n <Box flexDirection=\"row\" marginLeft={4}>\n <Text color={theme.mutedText}>\n Press <Text bold>Ctrl+O</Text> to expand details\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * SubagentTodosSection - Renders subagent's subtasks\n */\nfunction SubagentTodosSection({\n todos,\n theme,\n}: {\n todos: TodoItem[]\n theme: ReturnType<typeof getTheme>\n}) {\n return (\n <Box flexDirection=\"column\" marginLeft={1} marginY={1}>\n <Box flexDirection=\"row\">\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text color={theme.dimmedText}>Subtasks:</Text>\n </Box>\n {todos.map((todo, idx) => (\n <Box key={idx} flexDirection=\"row\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text>{getTodoStatusSymbol(todo.status)} </Text>\n <Text\n color={todo.status === 'completed' ? theme.dim : undefined}\n strikethrough={todo.status === 'completed'}\n >\n {todo.content}\n </Text>\n </Box>\n ))}\n </Box>\n )\n}\n\n/**\n * Helper: Extract todos from subagent messages\n * (This would be populated from TodoWriteTool calls within the subagent)\n */\nfunction extractSubagentTodos(subagent: SubagentState): TodoItem[] | null {\n // TODO: Implement extraction of todos from subagent messages\n // For now, return null (will be implemented in Phase 4)\n return null\n}\n\n/**\n * Helper: Extract thinking message from subagent\n */\nfunction extractThinkingMessage(\n subagent: SubagentState,\n): { message: string; startTime: number } | null {\n // Find thinking blocks in messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const thinkingBlock = msg.message.content.find(\n (block: any) => block.type === 'thinking',\n )\n if (thinkingBlock && 'thinking' in thinkingBlock) {\n return {\n message: thinkingBlock.thinking || 'Thinking...',\n startTime: subagent.metrics.startTime,\n }\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Extract tool executions from subagent\n */\nfunction extractToolExecutions(subagent: SubagentState): Array<{\n id: string\n name: string\n input: any\n status: 'pending' | 'running' | 'completed' | 'error'\n startTime: number\n durationMs?: number\n}> {\n const tools: Array<any> = []\n\n // Extract tool_use blocks from messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const toolBlocks = msg.message.content.filter(\n (block: any) => block.type === 'tool_use',\n )\n for (const toolBlock of toolBlocks) {\n if ('name' in toolBlock && 'id' in toolBlock) {\n tools.push({\n id: toolBlock.id,\n name: toolBlock.name,\n input: toolBlock.input,\n status: 'completed', // Simplified for now\n startTime: subagent.metrics.startTime,\n durationMs: 100, // Placeholder\n })\n }\n }\n }\n }\n\n return tools\n}\n\n/**\n * Helper: Extract response message from subagent\n */\nfunction extractResponse(subagent: SubagentState): AssistantMessage | null {\n // Find last assistant message with text content\n for (let i = subagent.messages.length - 1; i >= 0; i--) {\n const msg = subagent.messages[i]\n if (msg.type === 'assistant' && 'message' in msg) {\n const hasText = msg.message.content.some(\n (block: any) => block.type === 'text' && block.text.trim(),\n )\n if (hasText) {\n return msg as AssistantMessage\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Get status indicator symbol\n */\nfunction getStatusIndicator(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return '\u25CB'\n case 'queued':\n return '\u2139'\n case 'running':\n return '\u22EF'\n case 'completed':\n return '\u2713'\n case 'error':\n return '\u2716'\n }\n}\n\n/**\n * Helper: Get status color\n */\nfunction getStatusColor(\n status: SubagentState['status'],\n theme: ReturnType<typeof getTheme>,\n): string {\n switch (status) {\n case 'completed':\n return theme.success\n case 'error':\n return theme.error\n case 'running':\n return theme.brand\n default:\n return theme.dimmedText\n }\n}\n\n/**\n * Helper: Get status label\n */\nfunction getStatusLabel(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return 'Initializing...'\n case 'queued':\n return 'Queued'\n case 'running':\n return 'Running...'\n case 'completed':\n return 'Done'\n case 'error':\n return 'Error'\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useRef } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { getTheme } from "../utils/theme.js";
|
|
4
|
-
import {
|
|
4
|
+
import { formatDuration } from "../utils/format.js";
|
|
5
5
|
import { getAgentColor } from "../constants/colors.js";
|
|
6
6
|
import { SpinnerSymbol } from "./SpinnerSymbol.js";
|
|
7
7
|
import { Message } from "./Message.js";
|
|
8
8
|
import { useUnifiedAnimation } from "../utils/animationManager.js";
|
|
9
|
+
import { useAgentTokenStats, formatTokenCount } from "../hooks/useAgentTokenStats.js";
|
|
9
10
|
function SubagentProgress({
|
|
10
11
|
subagent,
|
|
11
12
|
isExpanded,
|
|
@@ -15,6 +16,8 @@ function SubagentProgress({
|
|
|
15
16
|
}) {
|
|
16
17
|
const theme = getTheme();
|
|
17
18
|
const { metrics, taskName, status, messages, agentType, agentColor } = subagent;
|
|
19
|
+
const tokenStats = useAgentTokenStats(subagent.id);
|
|
20
|
+
const tokenCount = tokenStats?.grandTotalTokens ?? 0;
|
|
18
21
|
const color = getAgentColor(agentColor, theme);
|
|
19
22
|
const isRunning = status === "initializing" || status === "running" || status === "queued";
|
|
20
23
|
const startTimeRef = useRef(metrics.startTime);
|
|
@@ -26,9 +29,9 @@ function SubagentProgress({
|
|
|
26
29
|
});
|
|
27
30
|
const duration = metrics.endTime ? metrics.endTime - metrics.startTime : Date.now() - metrics.startTime;
|
|
28
31
|
const statusText = getStatusText(status);
|
|
29
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 0 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(SpinnerSymbol, { isRunning, color: agentColor }), /* @__PURE__ */ React.createElement(Text, null, " Task(", taskName, ")")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.dim }, " \u23BFS1 "), /* @__PURE__ */ React.createElement(Text, { color: getStatusColor(status, theme) }, statusText), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, metrics.toolUseCount, " tool use", metrics.toolUseCount !== 1 ? "s" : ""), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText },
|
|
32
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 0 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(SpinnerSymbol, { isRunning, color: agentColor }), /* @__PURE__ */ React.createElement(Text, null, " Task(", taskName, ")")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.dim }, " \u23BFS1 "), /* @__PURE__ */ React.createElement(Text, { color: getStatusColor(status, theme) }, statusText), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, metrics.toolUseCount, " tool use", metrics.toolUseCount !== 1 ? "s" : ""), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, formatTokenCount(tokenCount), " tokens"), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, formatDuration(duration)), !isExpanded && /* @__PURE__ */ React.createElement(Text, { color: theme.dim }, " \xB7 (ctrl-o to expand)")), isExpanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginTop: 0 }, renderExpandedView(subagent, theme, tools, verbose, tokenCount)));
|
|
30
33
|
}
|
|
31
|
-
function renderExpandedView(subagent, theme, tools, verbose) {
|
|
34
|
+
function renderExpandedView(subagent, theme, tools, verbose, tokenCount) {
|
|
32
35
|
const { prompt, messages, status, metrics } = subagent;
|
|
33
36
|
const duration = metrics.endTime ? metrics.endTime - metrics.startTime : Date.now() - metrics.startTime;
|
|
34
37
|
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, prompt && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.dim }, " \u23BFS2 "), /* @__PURE__ */ React.createElement(Text, null, "Prompt:")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 7 }, /* @__PURE__ */ React.createElement(Text, null, prompt))), messages.map((msg, idx) => {
|
|
@@ -58,7 +61,7 @@ function renderExpandedView(subagent, theme, tools, verbose) {
|
|
|
58
61
|
}).filter((item) => item !== null);
|
|
59
62
|
}
|
|
60
63
|
return null;
|
|
61
|
-
}).flat().filter((item) => item !== null), status === "completed" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.dim }, " \u23BFS6 "), /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "Done"), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " ", "(", metrics.toolUseCount, " tool use", metrics.toolUseCount !== 1 ? "s" : "", " \xB7", " ",
|
|
64
|
+
}).flat().filter((item) => item !== null), status === "completed" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.dim }, " \u23BFS6 "), /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "Done"), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, " ", "(", metrics.toolUseCount, " tool use", metrics.toolUseCount !== 1 ? "s" : "", " \xB7", " ", formatTokenCount(tokenCount), " tokens \xB7 ", formatDuration(duration), ")")));
|
|
62
65
|
}
|
|
63
66
|
function getStatusText(status) {
|
|
64
67
|
switch (status) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/SubagentProgress.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useRef } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { formatNumber, formatDuration } from '@utils/format'\nimport { SYMBOLS } from '@constants/symbols'\nimport { getAgentColor } from '@constants/colors'\nimport { SpinnerSymbol } from './SpinnerSymbol'\nimport type { SubagentState } from '@minto-types/subagent'\nimport { Message } from './Message'\nimport type { Tool } from '@tool'\nimport { useUnifiedAnimation } from '@utils/animationManager'\n\ninterface SubagentProgressProps {\n subagent: SubagentState\n isExpanded: boolean\n tools?: Tool[]\n verbose?: boolean\n debug?: boolean\n}\n\n/**\n * \u5355\u4E2A Subagent \u7684\u8FDB\u5EA6\u663E\u793A\u7EC4\u4EF6\n *\n * \u7EDF\u4E00\u663E\u793A\u683C\u5F0F\uFF1A\n * - \u7B2C1\u884C\uFF1ASpinner + Task\u540D\u79F0 + Agent\u7C7B\u578B + Status + Metrics\u6458\u8981\n * - Ctrl+O\u5C55\u5F00\u540E\uFF1A\u663E\u793A\u8BE6\u7EC6\u7684\u6D88\u606F\u6D41\n *\n * \u4F7F\u7528\u7EDF\u4E00\u52A8\u753B\u7BA1\u7406\u5668\u4EE3\u66FF setInterval\uFF0C\u51CF\u5C11\u5C4F\u5E55\u95EA\u70C1\n */\nexport function SubagentProgress({\n subagent,\n isExpanded,\n tools = [],\n verbose = false,\n debug = false,\n}: SubagentProgressProps) {\n const theme = getTheme()\n const { metrics, taskName, status, messages, agentType, agentColor } =\n subagent\n\n // \u83B7\u53D6 agent \u989C\u8272\n const color = getAgentColor(agentColor, theme)\n\n // \u5224\u65AD\u662F\u5426\u6B63\u5728\u8FD0\u884C\uFF08\u51B3\u5B9A\u662F\u5426\u95EA\u70C1\uFF09\n const isRunning =\n status === 'initializing' || status === 'running' || status === 'queued'\n\n // Use unified animation manager for periodic time updates\n const startTimeRef = useRef(metrics.startTime)\n const { elapsedTime } = useUnifiedAnimation({\n enabled: isRunning,\n startTime: startTimeRef.current,\n spinnerFrameCount: 1,\n componentId: `subagent-${subagent.id}`,\n })\n\n // \u8BA1\u7B97\u6301\u7EED\u65F6\u95F4\uFF08\u5728\u6BCF\u6B21\u6E32\u67D3\u65F6\u91CD\u65B0\u8BA1\u7B97\uFF09\n const duration = metrics.endTime\n ? metrics.endTime - metrics.startTime\n : Date.now() - metrics.startTime\n\n // \u72B6\u6001\u6587\u672C\n const statusText = getStatusText(status)\n\n return (\n <Box flexDirection=\"column\" marginY={0}>\n {/* \u7B2C1\u884C\uFF1A\u4EFB\u52A1\u540D\u79F0 */}\n <Box flexDirection=\"row\">\n <SpinnerSymbol isRunning={isRunning} color={agentColor} />\n <Text> Task({taskName})</Text>\n </Box>\n\n {/* \u7B2C2\u884C\uFF1A\u8BE6\u7EC6\u4FE1\u606F\uFF08\u72B6\u6001\u3001\u6307\u6807\uFF09 - Claude Code CLI \u6807\u51C6\u683C\u5F0F */}\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS1 </Text>\n <Text color={getStatusColor(status, theme)}>{statusText}</Text>\n <Text color={theme.secondaryText}> \u00B7 </Text>\n <Text color={theme.secondaryText}>\n {metrics.toolUseCount} tool use{metrics.toolUseCount !== 1 ? 's' : ''}\n </Text>\n <Text color={theme.secondaryText}> \u00B7 </Text>\n <Text color={theme.secondaryText}>\n {formatNumber(metrics.tokenCount)} tokens\n </Text>\n <Text color={theme.secondaryText}> \u00B7 </Text>\n <Text color={theme.secondaryText}>{formatDuration(duration)}</Text>\n {!isExpanded && <Text color={theme.dim}> \u00B7 (ctrl-o to expand)</Text>}\n </Box>\n\n {/* Ctrl+O \u5C55\u5F00\u540E\u663E\u793A\u8BE6\u7EC6\u6D88\u606F\u6D41 - Claude Code CLI \u6807\u51C6\u683C\u5F0F */}\n {isExpanded && (\n <Box flexDirection=\"column\" marginTop={0}>\n {renderExpandedView(subagent, theme, tools, verbose)}\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * \u6E32\u67D3\u5C55\u5F00\u89C6\u56FE - Claude Code CLI \u6807\u51C6\u683C\u5F0F\n */\nfunction renderExpandedView(\n subagent: SubagentState,\n theme: ReturnType<typeof getTheme>,\n tools: Tool[],\n verbose: boolean,\n) {\n const { prompt, messages, status, metrics } = subagent\n const duration = metrics.endTime\n ? metrics.endTime - metrics.startTime\n : Date.now() - metrics.startTime\n\n return (\n <Box flexDirection=\"column\">\n {/* Prompt \u90E8\u5206 */}\n {prompt && (\n <Box flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS2 </Text>\n <Text>Prompt:</Text>\n </Box>\n <Box flexDirection=\"column\" marginLeft={7}>\n <Text>{prompt}</Text>\n </Box>\n </Box>\n )}\n\n {/* \u5DE5\u5177\u8C03\u7528\u3001\u7ED3\u679C\u548C\u54CD\u5E94 */}\n {messages\n .map((msg, idx) => {\n if (msg.type === 'assistant') {\n return msg.message.content\n .map((content, contentIdx) => {\n if (content.type === 'tool_use') {\n const tool = tools.find(t => t.name === content.name)\n const toolName =\n (tool?.userFacingName &&\n typeof tool.userFacingName === 'function'\n ? tool.userFacingName()\n : undefined) || content.name\n return (\n <Box key={`${idx}-${contentIdx}`} flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS3 </Text>\n <Text>{toolName}</Text>\n </Box>\n )\n } else if (content.type === 'text' && content.text.trim()) {\n // \u663E\u793A Response \u6587\u672C\n const lines = content.text.trim().split('\\n')\n return (\n <Box key={`${idx}-${contentIdx}`} flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS4 </Text>\n <Text>Response:</Text>\n </Box>\n <Box flexDirection=\"column\" marginLeft={7}>\n {lines.map((line, lineIdx) => (\n <Text>{line}</Text>\n ))}\n </Box>\n </Box>\n )\n }\n return null\n })\n .filter(item => item !== null)\n } else if (msg.type === 'user') {\n // \u5DE5\u5177\u7ED3\u679C\n return msg.message.content\n .map((content, contentIdx) => {\n if (content.type === 'tool_result') {\n const resultContent = Array.isArray(content.content)\n ? content.content.find(c => c.type === 'text')?.text\n : typeof content.content === 'string'\n ? content.content\n : ''\n\n if (resultContent && resultContent.trim()) {\n const lines = resultContent.trim().split('\\n')\n const preview =\n lines.length > 3\n ? lines.slice(0, 3).join('\\n') + '\\n...'\n : resultContent.trim()\n return (\n <Box key={`${idx}-${contentIdx}`} flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS5 </Text>\n <Text color={theme.secondaryText}>\n {preview.split('\\n')[0]}\n </Text>\n </Box>\n {preview\n .split('\\n')\n .slice(1)\n .map((line, lineIdx) => (\n <Box key={lineIdx} marginLeft={7}>\n <Text color={theme.secondaryText}>{line}</Text>\n </Box>\n ))}\n </Box>\n )\n }\n }\n return null\n })\n .filter(item => item !== null)\n }\n return null\n })\n .flat()\n .filter(item => item !== null)}\n\n {/* Done \u72B6\u6001\uFF08\u4EC5\u5728\u5B8C\u6210\u65F6\u663E\u793A\uFF09 */}\n {status === 'completed' && (\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS6 </Text>\n <Text color={theme.success}>Done</Text>\n <Text color={theme.secondaryText}>\n {' '}\n ({metrics.toolUseCount} tool use\n {metrics.toolUseCount !== 1 ? 's' : ''} \u00B7{' '}\n {formatNumber(metrics.tokenCount)} tokens \u00B7{' '}\n {formatDuration(duration)})\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * \u83B7\u53D6\u72B6\u6001\u6587\u672C\n */\nfunction getStatusText(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return 'Initialize\u2026'\n case 'queued':\n return 'Queued\u2026'\n case 'running':\n return 'In progress\u2026'\n case 'completed':\n return 'Done'\n case 'error':\n return 'Error'\n }\n}\n\n/**\n * \u83B7\u53D6\u72B6\u6001\u989C\u8272\n */\nfunction getStatusColor(\n status: SubagentState['status'],\n theme: ReturnType<typeof getTheme>,\n): string {\n switch (status) {\n case 'initializing':\n return theme.secondaryText\n case 'queued':\n return theme.secondaryText\n case 'running':\n return theme.minto\n case 'completed':\n return theme.success\n case 'error':\n return theme.error\n }\n}\n\n/**\n * \u6E32\u67D3\u6D88\u606F\u9884\u89C8 - \u4F7F\u7528\u5B8C\u6574\u7684 Message \u7EC4\u4EF6\u6765\u663E\u793A\u5DE5\u5177\u8C03\u7528\u548C\u7ED3\u679C\n */\nfunction renderMessagePreview(\n message: any,\n theme: ReturnType<typeof getTheme>,\n tools: Tool[],\n verbose: boolean,\n) {\n // \u4F7F\u7528 Message \u7EC4\u4EF6\u6765\u6E32\u67D3\uFF0C\u8FD9\u6837\u53EF\u4EE5\u663E\u793A\u5B8C\u6574\u7684\u5DE5\u5177\u8C03\u7528\u548C\u7ED3\u679C\n return (\n <Box flexDirection=\"column\" marginLeft={2}>\n <Message\n message={message}\n messages={[message]}\n addMargin={false}\n tools={tools}\n verbose={verbose}\n debug={false}\n erroredToolUseIDs={new Set()}\n inProgressToolUseIDs={new Set()}\n unresolvedToolUseIDs={new Set()}\n shouldAnimate={false}\n shouldShowDot={false}\n />\n </Box>\n )\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,SAAS,cAAc;AAC9B,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB;AACzB,
|
|
4
|
+
"sourcesContent": ["import React, { useRef } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { formatNumber, formatDuration } from '@utils/format'\nimport { SYMBOLS } from '@constants/symbols'\nimport { getAgentColor } from '@constants/colors'\nimport { SpinnerSymbol } from './SpinnerSymbol'\nimport type { SubagentState } from '@minto-types/subagent'\nimport { Message } from './Message'\nimport type { Tool } from '@tool'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport { useAgentTokenStats, formatTokenCount } from '@hooks/useAgentTokenStats'\n\ninterface SubagentProgressProps {\n subagent: SubagentState\n isExpanded: boolean\n tools?: Tool[]\n verbose?: boolean\n debug?: boolean\n}\n\n/**\n * \u5355\u4E2A Subagent \u7684\u8FDB\u5EA6\u663E\u793A\u7EC4\u4EF6\n *\n * \u7EDF\u4E00\u663E\u793A\u683C\u5F0F\uFF1A\n * - \u7B2C1\u884C\uFF1ASpinner + Task\u540D\u79F0 + Agent\u7C7B\u578B + Status + Metrics\u6458\u8981\n * - Ctrl+O\u5C55\u5F00\u540E\uFF1A\u663E\u793A\u8BE6\u7EC6\u7684\u6D88\u606F\u6D41\n *\n * \u4F7F\u7528\u7EDF\u4E00\u52A8\u753B\u7BA1\u7406\u5668\u4EE3\u66FF setInterval\uFF0C\u51CF\u5C11\u5C4F\u5E55\u95EA\u70C1\n */\nexport function SubagentProgress({\n subagent,\n isExpanded,\n tools = [],\n verbose = false,\n debug = false,\n}: SubagentProgressProps) {\n const theme = getTheme()\n const { metrics, taskName, status, messages, agentType, agentColor } =\n subagent\n\n // Get real-time token stats from unified TokenStatsManager\n const tokenStats = useAgentTokenStats(subagent.id)\n const tokenCount = tokenStats?.grandTotalTokens ?? 0\n\n // \u83B7\u53D6 agent \u989C\u8272\n const color = getAgentColor(agentColor, theme)\n\n // \u5224\u65AD\u662F\u5426\u6B63\u5728\u8FD0\u884C\uFF08\u51B3\u5B9A\u662F\u5426\u95EA\u70C1\uFF09\n const isRunning =\n status === 'initializing' || status === 'running' || status === 'queued'\n\n // Use unified animation manager for periodic time updates\n const startTimeRef = useRef(metrics.startTime)\n const { elapsedTime } = useUnifiedAnimation({\n enabled: isRunning,\n startTime: startTimeRef.current,\n spinnerFrameCount: 1,\n componentId: `subagent-${subagent.id}`,\n })\n\n // \u8BA1\u7B97\u6301\u7EED\u65F6\u95F4\uFF08\u5728\u6BCF\u6B21\u6E32\u67D3\u65F6\u91CD\u65B0\u8BA1\u7B97\uFF09\n const duration = metrics.endTime\n ? metrics.endTime - metrics.startTime\n : Date.now() - metrics.startTime\n\n // \u72B6\u6001\u6587\u672C\n const statusText = getStatusText(status)\n\n return (\n <Box flexDirection=\"column\" marginY={0}>\n {/* \u7B2C1\u884C\uFF1A\u4EFB\u52A1\u540D\u79F0 */}\n <Box flexDirection=\"row\">\n <SpinnerSymbol isRunning={isRunning} color={agentColor} />\n <Text> Task({taskName})</Text>\n </Box>\n\n {/* \u7B2C2\u884C\uFF1A\u8BE6\u7EC6\u4FE1\u606F\uFF08\u72B6\u6001\u3001\u6307\u6807\uFF09 - Claude Code CLI \u6807\u51C6\u683C\u5F0F */}\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS1 </Text>\n <Text color={getStatusColor(status, theme)}>{statusText}</Text>\n <Text color={theme.secondaryText}> \u00B7 </Text>\n <Text color={theme.secondaryText}>\n {metrics.toolUseCount} tool use{metrics.toolUseCount !== 1 ? 's' : ''}\n </Text>\n <Text color={theme.secondaryText}> \u00B7 </Text>\n <Text color={theme.secondaryText}>\n {formatTokenCount(tokenCount)} tokens\n </Text>\n <Text color={theme.secondaryText}> \u00B7 </Text>\n <Text color={theme.secondaryText}>{formatDuration(duration)}</Text>\n {!isExpanded && <Text color={theme.dim}> \u00B7 (ctrl-o to expand)</Text>}\n </Box>\n\n {/* Ctrl+O \u5C55\u5F00\u540E\u663E\u793A\u8BE6\u7EC6\u6D88\u606F\u6D41 - Claude Code CLI \u6807\u51C6\u683C\u5F0F */}\n {isExpanded && (\n <Box flexDirection=\"column\" marginTop={0}>\n {renderExpandedView(subagent, theme, tools, verbose, tokenCount)}\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * \u6E32\u67D3\u5C55\u5F00\u89C6\u56FE - Claude Code CLI \u6807\u51C6\u683C\u5F0F\n */\nfunction renderExpandedView(\n subagent: SubagentState,\n theme: ReturnType<typeof getTheme>,\n tools: Tool[],\n verbose: boolean,\n tokenCount: number,\n) {\n const { prompt, messages, status, metrics } = subagent\n const duration = metrics.endTime\n ? metrics.endTime - metrics.startTime\n : Date.now() - metrics.startTime\n\n return (\n <Box flexDirection=\"column\">\n {/* Prompt \u90E8\u5206 */}\n {prompt && (\n <Box flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS2 </Text>\n <Text>Prompt:</Text>\n </Box>\n <Box flexDirection=\"column\" marginLeft={7}>\n <Text>{prompt}</Text>\n </Box>\n </Box>\n )}\n\n {/* \u5DE5\u5177\u8C03\u7528\u3001\u7ED3\u679C\u548C\u54CD\u5E94 */}\n {messages\n .map((msg, idx) => {\n if (msg.type === 'assistant') {\n return msg.message.content\n .map((content, contentIdx) => {\n if (content.type === 'tool_use') {\n const tool = tools.find(t => t.name === content.name)\n const toolName =\n (tool?.userFacingName &&\n typeof tool.userFacingName === 'function'\n ? tool.userFacingName()\n : undefined) || content.name\n return (\n <Box key={`${idx}-${contentIdx}`} flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS3 </Text>\n <Text>{toolName}</Text>\n </Box>\n )\n } else if (content.type === 'text' && content.text.trim()) {\n // \u663E\u793A Response \u6587\u672C\n const lines = content.text.trim().split('\\n')\n return (\n <Box key={`${idx}-${contentIdx}`} flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS4 </Text>\n <Text>Response:</Text>\n </Box>\n <Box flexDirection=\"column\" marginLeft={7}>\n {lines.map((line, lineIdx) => (\n <Text>{line}</Text>\n ))}\n </Box>\n </Box>\n )\n }\n return null\n })\n .filter(item => item !== null)\n } else if (msg.type === 'user') {\n // \u5DE5\u5177\u7ED3\u679C\n return msg.message.content\n .map((content, contentIdx) => {\n if (content.type === 'tool_result') {\n const resultContent = Array.isArray(content.content)\n ? content.content.find(c => c.type === 'text')?.text\n : typeof content.content === 'string'\n ? content.content\n : ''\n\n if (resultContent && resultContent.trim()) {\n const lines = resultContent.trim().split('\\n')\n const preview =\n lines.length > 3\n ? lines.slice(0, 3).join('\\n') + '\\n...'\n : resultContent.trim()\n return (\n <Box key={`${idx}-${contentIdx}`} flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS5 </Text>\n <Text color={theme.secondaryText}>\n {preview.split('\\n')[0]}\n </Text>\n </Box>\n {preview\n .split('\\n')\n .slice(1)\n .map((line, lineIdx) => (\n <Box key={lineIdx} marginLeft={7}>\n <Text color={theme.secondaryText}>{line}</Text>\n </Box>\n ))}\n </Box>\n )\n }\n }\n return null\n })\n .filter(item => item !== null)\n }\n return null\n })\n .flat()\n .filter(item => item !== null)}\n\n {/* Done \u72B6\u6001\uFF08\u4EC5\u5728\u5B8C\u6210\u65F6\u663E\u793A\uFF09 */}\n {status === 'completed' && (\n <Box flexDirection=\"row\">\n <Text color={theme.dim}> \u23BFS6 </Text>\n <Text color={theme.success}>Done</Text>\n <Text color={theme.secondaryText}>\n {' '}\n ({metrics.toolUseCount} tool use\n {metrics.toolUseCount !== 1 ? 's' : ''} \u00B7{' '}\n {formatTokenCount(tokenCount)} tokens \u00B7 {formatDuration(duration)})\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * \u83B7\u53D6\u72B6\u6001\u6587\u672C\n */\nfunction getStatusText(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return 'Initialize\u2026'\n case 'queued':\n return 'Queued\u2026'\n case 'running':\n return 'In progress\u2026'\n case 'completed':\n return 'Done'\n case 'error':\n return 'Error'\n }\n}\n\n/**\n * \u83B7\u53D6\u72B6\u6001\u989C\u8272\n */\nfunction getStatusColor(\n status: SubagentState['status'],\n theme: ReturnType<typeof getTheme>,\n): string {\n switch (status) {\n case 'initializing':\n return theme.secondaryText\n case 'queued':\n return theme.secondaryText\n case 'running':\n return theme.minto\n case 'completed':\n return theme.success\n case 'error':\n return theme.error\n }\n}\n\n/**\n * \u6E32\u67D3\u6D88\u606F\u9884\u89C8 - \u4F7F\u7528\u5B8C\u6574\u7684 Message \u7EC4\u4EF6\u6765\u663E\u793A\u5DE5\u5177\u8C03\u7528\u548C\u7ED3\u679C\n */\nfunction renderMessagePreview(\n message: any,\n theme: ReturnType<typeof getTheme>,\n tools: Tool[],\n verbose: boolean,\n) {\n // \u4F7F\u7528 Message \u7EC4\u4EF6\u6765\u6E32\u67D3\uFF0C\u8FD9\u6837\u53EF\u4EE5\u663E\u793A\u5B8C\u6574\u7684\u5DE5\u5177\u8C03\u7528\u548C\u7ED3\u679C\n return (\n <Box flexDirection=\"column\" marginLeft={2}>\n <Message\n message={message}\n messages={[message]}\n addMargin={false}\n tools={tools}\n verbose={verbose}\n debug={false}\n erroredToolUseIDs={new Set()}\n inProgressToolUseIDs={new Set()}\n unresolvedToolUseIDs={new Set()}\n shouldAnimate={false}\n shouldShowDot={false}\n />\n </Box>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,SAAS,cAAc;AAC9B,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB;AACzB,SAAuB,sBAAsB;AAE7C,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAE9B,SAAS,eAAe;AAExB,SAAS,2BAA2B;AACpC,SAAS,oBAAoB,wBAAwB;AAmB9C,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ,CAAC;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AACV,GAA0B;AACxB,QAAM,QAAQ,SAAS;AACvB,QAAM,EAAE,SAAS,UAAU,QAAQ,UAAU,WAAW,WAAW,IACjE;AAGF,QAAM,aAAa,mBAAmB,SAAS,EAAE;AACjD,QAAM,aAAa,YAAY,oBAAoB;AAGnD,QAAM,QAAQ,cAAc,YAAY,KAAK;AAG7C,QAAM,YACJ,WAAW,kBAAkB,WAAW,aAAa,WAAW;AAGlE,QAAM,eAAe,OAAO,QAAQ,SAAS;AAC7C,QAAM,EAAE,YAAY,IAAI,oBAAoB;AAAA,IAC1C,SAAS;AAAA,IACT,WAAW,aAAa;AAAA,IACxB,mBAAmB;AAAA,IACnB,aAAa,YAAY,SAAS,EAAE;AAAA,EACtC,CAAC;AAGD,QAAM,WAAW,QAAQ,UACrB,QAAQ,UAAU,QAAQ,YAC1B,KAAK,IAAI,IAAI,QAAQ;AAGzB,QAAM,aAAa,cAAc,MAAM;AAEvC,SACE,oCAAC,OAAI,eAAc,UAAS,SAAS,KAEnC,oCAAC,OAAI,eAAc,SACjB,oCAAC,iBAAc,WAAsB,OAAO,YAAY,GACxD,oCAAC,YAAK,UAAO,UAAS,GAAC,CACzB,GAGA,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,OAAK,YAAK,GAC7B,oCAAC,QAAK,OAAO,eAAe,QAAQ,KAAK,KAAI,UAAW,GACxD,oCAAC,QAAK,OAAO,MAAM,iBAAe,QAAG,GACrC,oCAAC,QAAK,OAAO,MAAM,iBAChB,QAAQ,cAAa,aAAU,QAAQ,iBAAiB,IAAI,MAAM,EACrE,GACA,oCAAC,QAAK,OAAO,MAAM,iBAAe,QAAG,GACrC,oCAAC,QAAK,OAAO,MAAM,iBAChB,iBAAiB,UAAU,GAAE,SAChC,GACA,oCAAC,QAAK,OAAO,MAAM,iBAAe,QAAG,GACrC,oCAAC,QAAK,OAAO,MAAM,iBAAgB,eAAe,QAAQ,CAAE,GAC3D,CAAC,cAAc,oCAAC,QAAK,OAAO,MAAM,OAAK,0BAAqB,CAC/D,GAGC,cACC,oCAAC,OAAI,eAAc,UAAS,WAAW,KACpC,mBAAmB,UAAU,OAAO,OAAO,SAAS,UAAU,CACjE,CAEJ;AAEJ;AAKA,SAAS,mBACP,UACA,OACA,OACA,SACA,YACA;AACA,QAAM,EAAE,QAAQ,UAAU,QAAQ,QAAQ,IAAI;AAC9C,QAAM,WAAW,QAAQ,UACrB,QAAQ,UAAU,QAAQ,YAC1B,KAAK,IAAI,IAAI,QAAQ;AAEzB,SACE,oCAAC,OAAI,eAAc,YAEhB,UACC,oCAAC,OAAI,eAAc,YACjB,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,OAAK,YAAK,GAC7B,oCAAC,YAAK,SAAO,CACf,GACA,oCAAC,OAAI,eAAc,UAAS,YAAY,KACtC,oCAAC,YAAM,MAAO,CAChB,CACF,GAID,SACE,IAAI,CAAC,KAAK,QAAQ;AACjB,QAAI,IAAI,SAAS,aAAa;AAC5B,aAAO,IAAI,QAAQ,QAChB,IAAI,CAAC,SAAS,eAAe;AAC5B,YAAI,QAAQ,SAAS,YAAY;AAC/B,gBAAM,OAAO,MAAM,KAAK,OAAK,EAAE,SAAS,QAAQ,IAAI;AACpD,gBAAM,YACH,MAAM,kBACP,OAAO,KAAK,mBAAmB,aAC3B,KAAK,eAAe,IACpB,WAAc,QAAQ;AAC5B,iBACE,oCAAC,OAAI,KAAK,GAAG,GAAG,IAAI,UAAU,IAAI,eAAc,SAC9C,oCAAC,QAAK,OAAO,MAAM,OAAK,YAAK,GAC7B,oCAAC,YAAM,QAAS,CAClB;AAAA,QAEJ,WAAW,QAAQ,SAAS,UAAU,QAAQ,KAAK,KAAK,GAAG;AAEzD,gBAAM,QAAQ,QAAQ,KAAK,KAAK,EAAE,MAAM,IAAI;AAC5C,iBACE,oCAAC,OAAI,KAAK,GAAG,GAAG,IAAI,UAAU,IAAI,eAAc,YAC9C,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,OAAK,YAAK,GAC7B,oCAAC,YAAK,WAAS,CACjB,GACA,oCAAC,OAAI,eAAc,UAAS,YAAY,KACrC,MAAM,IAAI,CAAC,MAAM,YAChB,oCAAC,YAAM,IAAK,CACb,CACH,CACF;AAAA,QAEJ;AACA,eAAO;AAAA,MACT,CAAC,EACA,OAAO,UAAQ,SAAS,IAAI;AAAA,IACjC,WAAW,IAAI,SAAS,QAAQ;AAE9B,aAAO,IAAI,QAAQ,QAChB,IAAI,CAAC,SAAS,eAAe;AAC5B,YAAI,QAAQ,SAAS,eAAe;AAClC,gBAAM,gBAAgB,MAAM,QAAQ,QAAQ,OAAO,IAC/C,QAAQ,QAAQ,KAAK,OAAK,EAAE,SAAS,MAAM,GAAG,OAC9C,OAAO,QAAQ,YAAY,WACzB,QAAQ,UACR;AAEN,cAAI,iBAAiB,cAAc,KAAK,GAAG;AACzC,kBAAM,QAAQ,cAAc,KAAK,EAAE,MAAM,IAAI;AAC7C,kBAAM,UACJ,MAAM,SAAS,IACX,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,IAAI,UAC/B,cAAc,KAAK;AACzB,mBACE,oCAAC,OAAI,KAAK,GAAG,GAAG,IAAI,UAAU,IAAI,eAAc,YAC9C,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,OAAK,YAAK,GAC7B,oCAAC,QAAK,OAAO,MAAM,iBAChB,QAAQ,MAAM,IAAI,EAAE,CAAC,CACxB,CACF,GACC,QACE,MAAM,IAAI,EACV,MAAM,CAAC,EACP,IAAI,CAAC,MAAM,YACV,oCAAC,OAAI,KAAK,SAAS,YAAY,KAC7B,oCAAC,QAAK,OAAO,MAAM,iBAAgB,IAAK,CAC1C,CACD,CACL;AAAA,UAEJ;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC,EACA,OAAO,UAAQ,SAAS,IAAI;AAAA,IACjC;AACA,WAAO;AAAA,EACT,CAAC,EACA,KAAK,EACL,OAAO,UAAQ,SAAS,IAAI,GAG9B,WAAW,eACV,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,OAAK,YAAK,GAC7B,oCAAC,QAAK,OAAO,MAAM,WAAS,MAAI,GAChC,oCAAC,QAAK,OAAO,MAAM,iBAChB,KAAI,KACH,QAAQ,cAAa,aACtB,QAAQ,iBAAiB,IAAI,MAAM,IAAG,SAAG,KACzC,iBAAiB,UAAU,GAAE,iBAAW,eAAe,QAAQ,GAAE,GACpE,CACF,CAEJ;AAEJ;AAKA,SAAS,cAAc,QAAyC;AAC9D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAKA,SAAS,eACP,QACA,OACQ;AACR,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,EACjB;AACF;AAKA,SAAS,qBACP,SACA,OACA,OACA,SACA;AAEA,SACE,oCAAC,OAAI,eAAc,UAAS,YAAY,KACtC;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,UAAU,CAAC,OAAO;AAAA,MAClB,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,mBAAmB,oBAAI,IAAI;AAAA,MAC3B,sBAAsB,oBAAI,IAAI;AAAA,MAC9B,sBAAsB,oBAAI,IAAI;AAAA,MAC9B,eAAe;AAAA,MACf,eAAe;AAAA;AAAA,EACjB,CACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useRef } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { getTheme } from "../utils/theme.js";
|
|
4
|
-
import {
|
|
5
|
-
import { getAgentColor } from "../constants/colors.js";
|
|
4
|
+
import { formatDuration } from "../utils/format.js";
|
|
5
|
+
import { getAgentColor, SYMBOL_COLORS } from "../constants/colors.js";
|
|
6
6
|
import { useUnifiedAnimation } from "../utils/animationManager.js";
|
|
7
|
+
import { useAgentTokenStats, formatTokenCount } from "../hooks/useAgentTokenStats.js";
|
|
7
8
|
function TaskCard({
|
|
8
9
|
subagent,
|
|
9
10
|
isExpanded = false,
|
|
@@ -15,6 +16,8 @@ function TaskCard({
|
|
|
15
16
|
}) {
|
|
16
17
|
const theme = getTheme();
|
|
17
18
|
const { metrics, taskName, status, messages, agentType, agentColor } = subagent;
|
|
19
|
+
const tokenStats = useAgentTokenStats(subagent.id);
|
|
20
|
+
const tokenCount = tokenStats?.grandTotalTokens ?? 0;
|
|
18
21
|
const color = getAgentColor(agentColor, theme);
|
|
19
22
|
const isRunning = status === "initializing" || status === "running" || status === "queued";
|
|
20
23
|
const startTimeRef = useRef(metrics.startTime);
|
|
@@ -31,7 +34,7 @@ function TaskCard({
|
|
|
31
34
|
const latestMessage = getLatestMessage(messages);
|
|
32
35
|
const cardBgColor = isHighlighted ? theme.selectionBg : "#6A7B8E";
|
|
33
36
|
const isCompleted = status === "completed" || status === "error";
|
|
34
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 0 }, !isCompleted && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color }, statusIcon, " "), /* @__PURE__ */ React.createElement(Text, { bold: true }, taskName), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, " \xB7 ", agentType)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u251C\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusText), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, metrics.toolUseCount, " tool", metrics.toolUseCount !== 1 ? "s" : ""), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText },
|
|
37
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 0 }, !isCompleted && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color }, statusIcon, " "), /* @__PURE__ */ React.createElement(Text, { bold: true }, taskName), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, " \xB7 ", agentType)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u251C\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusText), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, metrics.toolUseCount, " tool", metrics.toolUseCount !== 1 ? "s" : ""), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, formatTokenCount(tokenCount), " tokens"), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, formatDuration(duration))), latestMessage && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, latestMessage)), messages.length > 0 && messages.map((msg, idx) => {
|
|
35
38
|
let content = "";
|
|
36
39
|
let prefix = "\u2190 ";
|
|
37
40
|
if (msg.type === "assistant") {
|
|
@@ -58,7 +61,7 @@ function TaskCard({
|
|
|
58
61
|
return null;
|
|
59
62
|
}
|
|
60
63
|
return /* @__PURE__ */ React.createElement(Box, { key: idx, flexDirection: "row", marginLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, prefix), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, content.substring(0, 80), content.length > 80 ? "..." : ""));
|
|
61
|
-
}).filter(Boolean)), isCompleted && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color }, statusIcon, " "), /* @__PURE__ */ React.createElement(Text, { bold: true }, taskName), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, " \xB7 ", agentType)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusText), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, metrics.toolUseCount, " tool", metrics.toolUseCount !== 1 ? "s" : ""), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText },
|
|
64
|
+
}).filter(Boolean)), isCompleted && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color }, statusIcon, " "), /* @__PURE__ */ React.createElement(Text, { bold: true }, taskName), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, " \xB7 ", agentType)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusText), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, metrics.toolUseCount, " tool", metrics.toolUseCount !== 1 ? "s" : ""), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, formatTokenCount(tokenCount), " tokens"), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, formatDuration(duration)), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, " (Ctrl+O to expand)")), isExpanded && messages.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 4, marginTop: 1 }, renderTaskOutputLog(subagent, theme, tools, verbose))), debug && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.info }, "[View Details]"), /* @__PURE__ */ React.createElement(Text, null, " "), isRunning && /* @__PURE__ */ React.createElement(Text, { color: SYMBOL_COLORS.error }, "[Cancel]")));
|
|
62
65
|
}
|
|
63
66
|
function getStatusIcon(status, isRunning) {
|
|
64
67
|
if (isRunning) {
|
|
@@ -95,16 +98,16 @@ function getStatusColor(status, theme) {
|
|
|
95
98
|
switch (status) {
|
|
96
99
|
case "initializing":
|
|
97
100
|
case "queued":
|
|
98
|
-
return
|
|
99
|
-
//
|
|
101
|
+
return SYMBOL_COLORS.pending;
|
|
102
|
+
// 灰色 - 等待
|
|
100
103
|
case "running":
|
|
101
|
-
return
|
|
102
|
-
//
|
|
104
|
+
return SYMBOL_COLORS.running;
|
|
105
|
+
// 粉紫 - 进行中
|
|
103
106
|
case "completed":
|
|
104
|
-
return
|
|
105
|
-
//
|
|
107
|
+
return SYMBOL_COLORS.success;
|
|
108
|
+
// 紫蓝 - 完成
|
|
106
109
|
case "error":
|
|
107
|
-
return
|
|
110
|
+
return SYMBOL_COLORS.error;
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
function getLatestMessage(messages) {
|