@@ -39,6 +39,17 @@ const ROLE_STYLE = {
3939 toolResult : { accent : "magenta" , label : "tool" } ,
4040} as const ;
4141
42+ /**
43+ * Per-tool name overrides for the toolResult header label. Default falls
44+ * back to the raw tool name (read_file, grep, shell, …) which is more
45+ * useful than a generic "tool". A few tools get friendlier presentation
46+ * labels because their raw name reads oddly in the gutter.
47+ */
48+ const TOOL_RESULT_LABEL : Record < string , string > = {
49+ shell : "bash" ,
50+ dispatch_agent : "subagent" ,
51+ } ;
52+
4253export function Message ( { message, streaming, width = 80 , tools } : MessageProps ) {
4354 const role = message . role ;
4455 const style = ROLE_STYLE [ role as keyof typeof ROLE_STYLE ] ;
@@ -48,6 +59,13 @@ export function Message({ message, streaming, width = 80, tools }: MessageProps)
4859 // gap, plus the parent App's paddingX of 1 each side. Reserve 4 cols so
4960 // the wrapped text never tries to occupy the accent gutter.
5061 const bodyWidth = Math . max ( 20 , width - 4 ) ;
62+ // Tool results carry the originating tool name on the message itself
63+ // (set by pi-agent-core). Surface that instead of the generic "tool"
64+ // label so users can see at a glance which tool produced this output.
65+ const headerLabel =
66+ role === "toolResult" && "toolName" in message && typeof message . toolName === "string"
67+ ? ( TOOL_RESULT_LABEL [ message . toolName ] ?? message . toolName )
68+ : style . label ;
5169
5270 return (
5371 < Box flexDirection = "row" marginY = { 0 } >
@@ -56,7 +74,7 @@ export function Message({ message, streaming, width = 80, tools }: MessageProps)
5674 </ Box >
5775 < Box flexDirection = "column" flexGrow = { 1 } >
5876 < Text color = { style . accent } bold >
59- { style . label }
77+ { headerLabel }
6078 { streaming ? " …" : "" }
6179 </ Text >
6280 < MessageBody message = { message } width = { bodyWidth } tools = { tools } />
@@ -97,7 +115,16 @@ function MessageBody({
97115 const text = message . content
98116 . map ( ( block ) => ( block . type === "text" ? block . text : `[image:${ block . mimeType } ]` ) )
99117 . join ( "" ) ;
100- return < TruncatedOutput text = { text } width = { width } keyPrefix = "tool" color = { message . isError ? "red" : undefined } /> ;
118+ const toolName = "toolName" in message && typeof message . toolName === "string" ? message . toolName : undefined ;
119+ return (
120+ < TruncatedOutput
121+ text = { text }
122+ width = { width }
123+ keyPrefix = "tool"
124+ color = { message . isError ? "red" : undefined }
125+ toolName = { toolName }
126+ />
127+ ) ;
101128 }
102129
103130 return null ;
@@ -545,12 +572,23 @@ function DiffSummary({ diff, width, keyPrefix }: { diff: DiffInfo; width: number
545572 ) ;
546573}
547574
548- const MAX_TOOL_OUTPUT_LINES = 12 ;
549- const HEAD_TOOL_OUTPUT_LINES = 8 ;
550- const TAIL_TOOL_OUTPUT_LINES = 3 ;
575+ const DEFAULT_MAX_TOOL_OUTPUT_LINES = 12 ;
576+
577+ /**
578+ * Per-tool display caps. Search-style tools (grep, find, glob) produce
579+ * many matches, most of which the user doesn't need to read inline —
580+ * the model still sees the full result. Default is 12 lines.
581+ */
582+ const TOOL_OUTPUT_LIMITS : Record < string , number > = {
583+ grep : 6 ,
584+ search_files : 6 ,
585+ glob : 8 ,
586+ find : 8 ,
587+ list_files : 10 ,
588+ } ;
551589
552590/**
553- * Truncate tool output past MAX_TOOL_OUTPUT_LINES into "head + (N hidden)
591+ * Truncate tool output past the per-tool limit into "head + (N hidden)
554592 * + tail" — long shell or grep output otherwise dominates the
555593 * transcript and pushes context off-screen. The agent still gets the
556594 * full output; this is purely a display trim. Errors are NEVER
@@ -561,19 +599,30 @@ function TruncatedOutput({
561599 width,
562600 keyPrefix,
563601 color,
602+ toolName,
564603} : {
565604 text : string ;
566605 width : number ;
567606 keyPrefix : string ;
568607 color ?: string ;
608+ toolName ?: string ;
569609} ) {
610+ const max =
611+ toolName && TOOL_OUTPUT_LIMITS [ toolName ] !== undefined
612+ ? TOOL_OUTPUT_LIMITS [ toolName ]
613+ : DEFAULT_MAX_TOOL_OUTPUT_LINES ;
614+ // Reserve at least 1 head + 1 tail line so the user can see the
615+ // shape of the truncation; rest is head-weighted (where the
616+ // interesting content usually is).
617+ const tailLines = max >= 8 ? 3 : 2 ;
618+ const headLines = Math . max ( 1 , max - tailLines - 1 ) ;
570619 const lines = text . split ( "\n" ) ;
571- if ( color === "red" || lines . length <= MAX_TOOL_OUTPUT_LINES ) {
620+ if ( color === "red" || lines . length <= max ) {
572621 return < WrappedLines text = { text } width = { width } keyPrefix = { keyPrefix } color = { color } /> ;
573622 }
574- const head = lines . slice ( 0 , HEAD_TOOL_OUTPUT_LINES ) . join ( "\n" ) ;
575- const tail = lines . slice ( lines . length - TAIL_TOOL_OUTPUT_LINES ) . join ( "\n" ) ;
576- const hidden = lines . length - HEAD_TOOL_OUTPUT_LINES - TAIL_TOOL_OUTPUT_LINES ;
623+ const head = lines . slice ( 0 , headLines ) . join ( "\n" ) ;
624+ const tail = lines . slice ( lines . length - tailLines ) . join ( "\n" ) ;
625+ const hidden = lines . length - headLines - tailLines ;
577626 return (
578627 < >
579628 < WrappedLines text = { head } width = { width } keyPrefix = { `${ keyPrefix } -h` } color = { color } />
0 commit comments