@@ -111,8 +111,18 @@ func (br baseRenderer) unmarshalParams(input string, target any) error {
111111 return json.Unmarshal([]byte(input), target)
112112}
113113
114+// makeHeader builds the tool call header with status icon and parameters for a nested tool call.
115+func (br baseRenderer) makeNestedHeader(v *toolCallCmp, tool string, width int, params ...string) string {
116+ t := styles.CurrentTheme()
117+ tool = t.S().Base.Foreground(t.FgHalfMuted).Render(tool) + " "
118+ return tool + renderParamList(true, width-lipgloss.Width(tool), params...)
119+}
120+
114121// makeHeader builds "<Tool>: param (key=value)" and truncates as needed.
115122func (br baseRenderer) makeHeader(v *toolCallCmp, tool string, width int, params ...string) string {
123+ if v.isNested {
124+ return br.makeNestedHeader(v, tool, width, params...)
125+ }
116126 t := styles.CurrentTheme()
117127 icon := t.S().Base.Foreground(t.GreenDark).Render(styles.ToolPending)
118128 if v.result.ToolCallID != "" {
@@ -125,16 +135,17 @@ func (br baseRenderer) makeHeader(v *toolCallCmp, tool string, width int, params
125135 icon = t.S().Muted.Render(styles.ToolPending)
126136 }
127137 tool = t.S().Base.Foreground(t.Blue).Render(tool)
128− prefix := fmt.Sprintf("%s %s: ", icon, tool)
129− return prefix + renderParamList(width-lipgloss.Width(prefix), params...)
138+ prefix := fmt.Sprintf("%s %s ", icon, tool)
139+ return prefix + renderParamList(false, width-lipgloss.Width(prefix), params...)
130140}
131141
132142// renderError provides consistent error rendering
133143func (br baseRenderer) renderError(v *toolCallCmp, message string) string {
134144 t := styles.CurrentTheme()
135145 header := br.makeHeader(v, prettifyToolName(v.call.Name), v.textWidth(), "")
136− message = t.S().Error.Render(v.fit(message, v.textWidth()-2)) // -2 for padding
137− return joinHeaderBody(header, message)
146+ errorTag := t.S().Base.Padding(0, 1).Background(t.Red).Foreground(t.White).Render("ERROR")
147+ message = t.S().Base.Foreground(t.FgHalfMuted).Render(v.fit(message, v.textWidth()-3-lipgloss.Width(errorTag))) // -2 for padding and space
148+ return joinHeaderBody(header, errorTag+" "+message)
138149}
139150
140151// Register tool renderers
@@ -188,6 +199,9 @@ func (br bashRenderer) Render(v *toolCallCmp) string {
188199 args := newParamBuilder().addMain(cmd).build()
189200
190201 return br.renderWithParams(v, "Bash", args, func() string {
202+ if v.result.Content == tools.BashNoOutput {
203+ return ""
204+ }
191205 return renderPlainContent(v, v.result.Content)
192206 })
193207}
@@ -244,13 +258,12 @@ type editRenderer struct {
244258// Render displays the edited file with a formatted diff of changes
245259func (er editRenderer) Render(v *toolCallCmp) string {
246260 var params tools.EditParams
247− if err := er.unmarshalParams(v.call.Input, ¶ms); err != nil {
248− return er.renderError(v, "Invalid edit parameters")
261+ var args []string
262+ if err := er.unmarshalParams(v.call.Input, ¶ms); err == nil {
263+ file := fsext.PrettyPath(params.FilePath)
264+ args = newParamBuilder().addMain(file).build()
249265 }
250266
251− file := fsext.PrettyPath(params.FilePath)
252− args := newParamBuilder().addMain(file).build()
253−
254267 return er.renderWithParams(v, "Edit", args, func() string {
255268 var meta tools.EditResponseMetadata
256269 if err := er.unmarshalParams(v.result.Metadata, &meta); err != nil {
@@ -260,8 +273,10 @@ func (er editRenderer) Render(v *toolCallCmp) string {
260273 formatter := core.DiffFormatter().
261274 Before(fsext.PrettyPath(params.FilePath), meta.OldContent).
262275 After(fsext.PrettyPath(params.FilePath), meta.NewContent).
263− Split().
264276 Width(v.textWidth() - 2) // -2 for padding
277+ if v.textWidth() > 120 {
278+ formatter = formatter.Split()
279+ }
265280 return formatter.String()
266281 })
267282}
@@ -278,13 +293,13 @@ type writeRenderer struct {
278293// Render displays the file being written with syntax highlighting
279294func (wr writeRenderer) Render(v *toolCallCmp) string {
280295 var params tools.WriteParams
281− if err := wr.unmarshalParams(v.call.Input, ¶ms); err != nil {
282− return wr.renderError(v, "Invalid write parameters")
296+ var args []string
297+ var file string
298+ if err := wr.unmarshalParams(v.call.Input, ¶ms); err == nil {
299+ file = fsext.PrettyPath(params.FilePath)
300+ args = newParamBuilder().addMain(file).build()
283301 }
284302
285− file := fsext.PrettyPath(params.FilePath)
286− args := newParamBuilder().addMain(file).build()
287−
288303 return wr.renderWithParams(v, "Write", args, func() string {
289304 return renderCodeContent(v, file, params.Content, 0)
290305 })
@@ -302,16 +317,15 @@ type fetchRenderer struct {
302317// Render displays the fetched URL with format and timeout parameters
303318func (fr fetchRenderer) Render(v *toolCallCmp) string {
304319 var params tools.FetchParams
305− if err := fr.unmarshalParams(v.call.Input, ¶ms); err != nil {
306− return fr.renderError(v, "Invalid fetch parameters")
320+ var args []string
321+ if err := fr.unmarshalParams(v.call.Input, ¶ms); err == nil {
322+ args = newParamBuilder().
323+ addMain(params.URL).
324+ addKeyValue("format", params.Format).
325+ addKeyValue("timeout", formatTimeout(params.Timeout)).
326+ build()
307327 }
308328
309− args := newParamBuilder().
310− addMain(params.URL).
311− addKeyValue("format", params.Format).
312− addKeyValue("timeout", formatTimeout(params.Timeout)).
313− build()
314−
315329 return fr.renderWithParams(v, "Fetch", args, func() string {
316330 file := fr.getFileExtension(params.Format)
317331 return renderCodeContent(v, file, v.result.Content, 0)
@@ -350,15 +364,14 @@ type globRenderer struct {
350364// Render displays the glob pattern with optional path parameter
351365func (gr globRenderer) Render(v *toolCallCmp) string {
352366 var params tools.GlobParams
353− if err := gr.unmarshalParams(v.call.Input, ¶ms); err != nil {
354− return gr.renderError(v, "Invalid glob parameters")
367+ var args []string
368+ if err := gr.unmarshalParams(v.call.Input, ¶ms); err == nil {
369+ args = newParamBuilder().
370+ addMain(params.Pattern).
371+ addKeyValue("path", params.Path).
372+ build()
355373 }
356374
357− args := newParamBuilder().
358− addMain(params.Pattern).
359− addKeyValue("path", params.Path).
360− build()
361−
362375 return gr.renderWithParams(v, "Glob", args, func() string {
363376 return renderPlainContent(v, v.result.Content)
364377 })
@@ -376,17 +389,16 @@ type grepRenderer struct {
376389// Render displays the search pattern with path, include, and literal text options
377390func (gr grepRenderer) Render(v *toolCallCmp) string {
378391 var params tools.GrepParams
379− if err := gr.unmarshalParams(v.call.Input, ¶ms); err != nil {
380− return gr.renderError(v, "Invalid grep parameters")
392+ var args []string
393+ if err := gr.unmarshalParams(v.call.Input, ¶ms); err == nil {
394+ args = newParamBuilder().
395+ addMain(params.Pattern).
396+ addKeyValue("path", params.Path).
397+ addKeyValue("include", params.Include).
398+ addFlag("literal", params.LiteralText).
399+ build()
381400 }
382401
383− args := newParamBuilder().
384− addMain(params.Pattern).
385− addKeyValue("path", params.Path).
386− addKeyValue("include", params.Include).
387− addFlag("literal", params.LiteralText).
388− build()
389−
390402 return gr.renderWithParams(v, "Grep", args, func() string {
391403 return renderPlainContent(v, v.result.Content)
392404 })
@@ -404,17 +416,16 @@ type lsRenderer struct {
404416// Render displays the directory path, defaulting to current directory
405417func (lr lsRenderer) Render(v *toolCallCmp) string {
406418 var params tools.LSParams
407− if err := lr.unmarshalParams(v.call.Input, ¶ms); err != nil {
408− return lr.renderError(v, "Invalid ls parameters")
409− }
419+ var args []string
420+ if err := lr.unmarshalParams(v.call.Input, ¶ms); err == nil {
421+ path := params.Path
422+ if path == "" {
423+ path = "."
424+ }
425+ path = fsext.PrettyPath(path)
410426
411− path := params.Path
412− if path == "" {
413− path = "."
427+ args = newParamBuilder().addMain(path).build()
414428 }
415− path = fsext.PrettyPath(path)
416−
417− args := newParamBuilder().addMain(path).build()
418429
419430 return lr.renderWithParams(v, "List", args, func() string {
420431 return renderPlainContent(v, v.result.Content)
@@ -433,16 +444,15 @@ type sourcegraphRenderer struct {
433444// Render displays the search query with optional count and context window parameters
434445func (sr sourcegraphRenderer) Render(v *toolCallCmp) string {
435446 var params tools.SourcegraphParams
436− if err := sr.unmarshalParams(v.call.Input, ¶ms); err != nil {
437− return sr.renderError(v, "Invalid sourcegraph parameters")
447+ var args []string
448+ if err := sr.unmarshalParams(v.call.Input, ¶ms); err == nil {
449+ args = newParamBuilder().
450+ addMain(params.Query).
451+ addKeyValue("count", formatNonZero(params.Count)).
452+ addKeyValue("context", formatNonZero(params.ContextWindow)).
453+ build()
438454 }
439455
440− args := newParamBuilder().
441− addMain(params.Query).
442− addKeyValue("count", formatNonZero(params.Count)).
443− addKeyValue("context", formatNonZero(params.ContextWindow)).
444− build()
445−
446456 return sr.renderWithParams(v, "Sourcegraph", args, func() string {
447457 return renderPlainContent(v, v.result.Content)
448458 })
@@ -475,25 +485,47 @@ type agentRenderer struct {
475485 baseRenderer
476486}
477487
488+func RoundedEnumerator(children tree.Children, index int) string {
489+ if children.Length()-1 == index {
490+ return " ╰──"
491+ }
492+ return " ├──"
493+}
494+
478495// Render displays agent task parameters and result content
479496func (tr agentRenderer) Render(v *toolCallCmp) string {
497+ t := styles.CurrentTheme()
480498 var params agent.AgentParams
481− if err := tr.unmarshalParams(v.call.Input, ¶ms); err != nil {
482− return tr.renderError(v, "Invalid task parameters")
483− }
499+ tr.unmarshalParams(v.call.Input, ¶ms)
500+
484501 prompt := params.Prompt
485502 prompt = strings.ReplaceAll(prompt, "\n", " ")
486− args := newParamBuilder().addMain(prompt).build()
487503
488− header := tr.makeHeader(v, "Task", v.textWidth(), args...)
489− t := tree.Root(header)
504+ header := tr.makeHeader(v, "Agent", v.textWidth())
505+ if res, done := earlyState(header, v); done {
506+ return res
507+ }
508+ taskTag := t.S().Base.Padding(0, 1).MarginLeft(1).Background(t.BlueLight).Foreground(t.White).Render("Task")
509+ remainingWidth := v.textWidth() - lipgloss.Width(header) - lipgloss.Width(taskTag) - 2 // -2 for padding
510+ prompt = t.S().Muted.Width(remainingWidth).Render(prompt)
511+ header = lipgloss.JoinVertical(
512+ lipgloss.Left,
513+ header,
514+ "",
515+ lipgloss.JoinHorizontal(
516+ lipgloss.Left,
517+ taskTag,
518+ " ",
519+ prompt,
520+ ),
521+ )
522+ childTools := tree.Root(header)
490523
491524 for _, call := range v.nestedToolCalls {
492− t.Child(call.View())
525+ childTools.Child(call.View())
493526 }
494−
495527 parts := []string{
496− t.Enumerator(tree.RoundedEnumerator).String(),
528+ childTools.Enumerator(RoundedEnumerator).String(),
497529 }
498530 if v.result.ToolCallID == "" {
499531 v.spinning = true
@@ -516,7 +548,8 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
516548}
517549
518550// renderParamList renders params, params[0] (params[1]=params[2] ....)
519−func renderParamList(paramsWidth int, params ...string) string {
551+func renderParamList(nested bool, paramsWidth int, params ...string) string {
552+ t := styles.CurrentTheme()
520553 if len(params) == 0 {
521554 return ""
522555 }
@@ -526,7 +559,10 @@ func renderParamList(paramsWidth int, params ...string) string {
526559 }
527560
528561 if len(params) == 1 {
529− return mainParam
562+ if nested {
563+ return t.S().Muted.Render(mainParam)
564+ }
565+ return t.S().Subtle.Render(mainParam)
530566 }
531567 otherParams := params[1:]
532568 // create pairs of key/value
@@ -547,15 +583,21 @@ func renderParamList(paramsWidth int, params ...string) string {
547583 partsRendered := strings.Join(parts, ", ")
548584 remainingWidth := paramsWidth - lipgloss.Width(partsRendered) - 3 // count for " ()"
549585 if remainingWidth < 30 {
586+ if nested {
587+ return t.S().Muted.Render(mainParam)
588+ }
550589 // No space for the params, just show the main
551− return mainParam
590+ return t.S().Subtle.Render(mainParam)
552591 }
553592
554593 if len(parts) > 0 {
555594 mainParam = fmt.Sprintf("%s (%s)", mainParam, strings.Join(parts, ", "))
556595 }
557596
558− return ansi.Truncate(mainParam, paramsWidth, "...")
597+ if nested {
598+ return t.S().Muted.Render(ansi.Truncate(mainParam, paramsWidth, "..."))
599+ }
600+ return t.S().Subtle.Render(ansi.Truncate(mainParam, paramsWidth, "..."))
559601}
560602
561603// earlyState returns immediately‑rendered error/cancelled/ongoing states.
@@ -566,21 +608,21 @@ func earlyState(header string, v *toolCallCmp) (string, bool) {
566608 case v.result.IsError:
567609 message = v.renderToolError()
568610 case v.cancelled:
569− message = "Cancelled"
611+ message = t.S().Base.Padding(0, 1).Background(t.Border).Render("Cancelled")
570612 case v.result.ToolCallID == "":
571− message = "Waiting for tool to start..."
613+ message = t.S().Base.Padding(0, 1).Background(t.Accent).Foreground(t.FgSubtle).Render("Waiting for tool to start...")
572614 default:
573615 return "", false
574616 }
575617
576618 message = t.S().Base.PaddingLeft(2).Render(message)
577− return lipgloss.JoinVertical(lipgloss.Left, header, message), true
619+ return lipgloss.JoinVertical(lipgloss.Left, header, "", message), true
578620}
579621
580622func joinHeaderBody(header, body string) string {
581623 t := styles.CurrentTheme()
582624 body = t.S().Base.PaddingLeft(2).Render(body)
583− return lipgloss.JoinVertical(lipgloss.Left, header, body, "")
625+ return lipgloss.JoinVertical(lipgloss.Left, header, "", body, "")
584626}
585627
586628func renderPlainContent(v *toolCallCmp, content string) string {
@@ -600,46 +642,56 @@ func renderPlainContent(v *toolCallCmp, content string) string {
600642 }
601643 out = append(out, t.S().Muted.
602644 Width(width).
603− Background(t.BgSubtle).
645+ Background(t.BgBaseLighter).
604646 Render(ln))
605647 }
606648
607649 if len(lines) > responseContextHeight {
608650 out = append(out, t.S().Muted.
609− Background(t.BgSubtle).
651+ Background(t.BgBaseLighter).
610652 Width(width).
611653 Render(fmt.Sprintf("... (%d lines)", len(lines)-responseContextHeight)))
612654 }
613655 return strings.Join(out, "\n")
614656}
615657
658+func pad(v any, width int) string {
659+ s := fmt.Sprintf("%v", v)
660+ w := ansi.StringWidth(s)
661+ if w >= width {
662+ return s
663+ }
664+ return strings.Repeat(" ", width-w) + s
665+}
666+
616667func renderCodeContent(v *toolCallCmp, path, content string, offset int) string {
617668 t := styles.CurrentTheme()
618669 truncated := truncateHeight(content, responseContextHeight)
619670
620− highlighted, _ := highlight.SyntaxHighlight(truncated, path, t.BgSubtle)
671+ highlighted, _ := highlight.SyntaxHighlight(truncated, path, t.BgBase)
621672 lines := strings.Split(highlighted, "\n")
622673
623674 if len(strings.Split(content, "\n")) > responseContextHeight {
624675 lines = append(lines, t.S().Muted.
625− Background(t.BgSubtle).
626− Width(v.textWidth()-2).
627− Render(fmt.Sprintf("... (%d lines)", len(strings.Split(content, "\n"))-responseContextHeight)))
676+ Background(t.BgBase).
677+ Render(fmt.Sprintf(" …(%d lines)", len(strings.Split(content, "\n"))-responseContextHeight)))
628678 }
629679
680+ maxLineNumber := len(lines) + offset
681+ padding := lipgloss.Width(fmt.Sprintf("%d", maxLineNumber))
630682 for i, ln := range lines {
631− num := t.S().Muted.
632− Background(t.BgSubtle).
633− PaddingLeft(4).
634− PaddingRight(2).
635− Render(fmt.Sprintf("%d", i+1+offset))
636− w := v.textWidth() - 2 - lipgloss.Width(num) // -2 for left padding
683+ num := t.S().Base.
684+ Foreground(t.FgMuted).
685+ Background(t.BgBase).
686+ PaddingRight(1).
687+ PaddingLeft(1).
688+ Render(pad(i+1+offset, padding))
689+ w := v.textWidth() - 10 - lipgloss.Width(num) // -4 for left padding
637690 lines[i] = lipgloss.JoinHorizontal(lipgloss.Left,
638691 num,
639692 t.S().Base.
640− Width(w).
641− Background(t.BgSubtle).
642− Render(v.fit(ln, w)))
693+ PaddingLeft(1).
694+ Render(v.fit(ln, w-1)))
643695 }
644696 return lipgloss.JoinVertical(lipgloss.Left, lines...)
645697}
@@ -647,8 +699,9 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
647699func (v *toolCallCmp) renderToolError() string {
648700 t := styles.CurrentTheme()
649701 err := strings.ReplaceAll(v.result.Content, "\n", " ")
650− err = fmt.Sprintf("Error: %s", err)
651− return t.S().Base.Foreground(t.Error).Render(v.fit(err, v.textWidth()))
702+ errTag := t.S().Base.Padding(0, 1).Background(t.Red).Foreground(t.White).Render("ERROR")
703+ err = fmt.Sprintf("%s %s", errTag, t.S().Base.Foreground(t.FgHalfMuted).Render(v.fit(err, v.textWidth()-2-lipgloss.Width(errTag))))
704+ return err
652705}
653706
654707func truncateHeight(s string, h int) string {
@@ -662,7 +715,7 @@ func truncateHeight(s string, h int) string {
662715func prettifyToolName(name string) string {
663716 switch name {
664717 case agent.AgentToolName:
665− return "Task"
718+ return "Agent"
666719 case tools.BashToolName:
667720 return "Bash"
668721 case tools.EditToolName: