diff --git a/ui-tui/src/components/messageLine.tsx b/ui-tui/src/components/messageLine.tsx index 7bdfb443b7..950b61b4d7 100644 --- a/ui-tui/src/components/messageLine.tsx +++ b/ui-tui/src/components/messageLine.tsx @@ -1,5 +1,5 @@ import { Ansi, Box, NoSelect, Text } from '@hermes/ink' -import { memo } from 'react' +import { memo, useState } from 'react' import { LONG_MSG } from '../config/limits.js' import { sectionMode } from '../domain/details.js' @@ -22,6 +22,9 @@ import { StreamingMd } from './streamingMarkdown.js' import { ToolTrail } from './thinking.js' import { TodoPanel } from './todoPanel.js' +// Collapse threshold for long system messages (system prompt etc.) +const SYSTEM_COLLAPSE_CHARS = 400 + export const MessageLine = memo(function MessageLine({ cols, compact, @@ -46,6 +49,10 @@ export const MessageLine = memo(function MessageLine({ const activityMode = sectionMode('activity', detailsMode, sections, detailsModeCommandOverride) const thinking = msg.thinking?.trim() ?? '' + // Collapse toggle for long system messages + const systemIsLong = msg.role === 'system' && msg.text.length > SYSTEM_COLLAPSE_CHARS + const [systemOpen, setSystemOpen] = useState(false) + if (msg.kind === 'trail' && msg.todos?.length) { return ( {msg.text} } + // ── Collapsible long system message (system prompt, AGENTS.md, etc.) ── + // MUST come before the hasAnsi check — system messages from the backend + // contain Rich markup escape codes that would otherwise hit full render. + if (systemIsLong) { + const firstLine = (msg.text.split('\n')[0] ?? '').trim().slice(0, 120) || '(system message)' + + return ( + + setSystemOpen(v => !v)}> + {systemOpen ? '▾ ' : '▸ '} + {firstLine} + + {' — '} + {msg.text.length.toLocaleString()} chars + + + {systemOpen && {msg.text}} + + ) + } + if (msg.role !== 'user' && hasAnsi(msg.text)) { return {msg.text} }