@@ -20,6 +20,7 @@ import (
2020 "context"
2121 "encoding/json"
2222 "fmt"
23+ "os"
2324 "strings"
2425 "time"
2526
@@ -52,6 +53,27 @@ var blocks = []string{"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
5253// Braille spinner frames (matching Rich's "dots" spinner)
5354var spinnerFrames = []string {"⠋" , "⠙" , "⠹" , "⠸" , "⠼" , "⠴" , "⠦" , "⠧" , "⠇" , "⠏" }
5455
56+ // startSpinner shows a braille spinner on stderr with the given message.
57+ // Returns a stop function that clears the spinner line.
58+ func startSpinner (msg string ) func () {
59+ done := make (chan struct {})
60+ go func () {
61+ i := 0
62+ for {
63+ select {
64+ case <- done :
65+ fmt .Fprintf (os .Stderr , "\r \033 [K" )
66+ return
67+ default :
68+ fmt .Fprintf (os .Stderr , "\r %s %s" , spinnerFrames [i % len (spinnerFrames )], msg )
69+ i ++
70+ time .Sleep (80 * time .Millisecond )
71+ }
72+ }
73+ }()
74+ return func () { close (done ) }
75+ }
76+
5577type consoleTickMsg struct {}
5678type sessionEventMsg struct { event * agent.AgentSessionEvent }
5779type sessionResponseMsg struct { resp * agent.SessionResponse }
@@ -388,11 +410,38 @@ func (m *consoleModel) handleSessionEvent(ev *agent.AgentSessionEvent) []tea.Cmd
388410 m .metricsText = text
389411 }
390412 }
391- lines := formatChatItem (item )
392- for _ , line := range lines {
393- cmds = append (cmds , tea .Println (line ))
413+ cmds = append (cmds , tea .Println (formatChatItem (item )))
414+ }
415+
416+ case * agent.AgentSessionEvent_FunctionToolsExecuted_ :
417+ ft := e .FunctionToolsExecuted
418+ outputsByCallID := make (map [string ]* agent.FunctionCallOutput )
419+ for _ , fco := range ft .FunctionCallOutputs {
420+ outputsByCallID [fco .CallId ] = fco
421+ }
422+ var b strings.Builder
423+ for i , fc := range ft .FunctionCalls {
424+ if i > 0 {
425+ b .WriteString ("\n " )
426+ }
427+ b .WriteString ("\n " )
428+ b .WriteString ("● " )
429+ b .WriteString ("function_tool: " )
430+ b .WriteString (fc .Name )
431+ if fco , ok := outputsByCallID [fc .CallId ]; ok {
432+ if fco .IsError {
433+ b .WriteString ("\n " )
434+ b .WriteString (redBoldStyle .Render ("✗ " ))
435+ b .WriteString (redStyle .Render (truncateOutput (fco .Output )))
436+ } else {
437+ b .WriteString ("\n " )
438+ b .WriteString (greenStyle .Render ("✓ " ))
439+ b .WriteString (dimStyle .Render (summarizeOutput (fco .Output )))
440+ }
394441 }
395442 }
443+ b .WriteString ("\n " )
444+ cmds = append (cmds , tea .Println (b .String ()))
396445
397446 case * agent.AgentSessionEvent_Error_ :
398447 cmds = append (cmds , tea .Println (
@@ -403,16 +452,12 @@ func (m *consoleModel) handleSessionEvent(ev *agent.AgentSessionEvent) []tea.Cmd
403452 return cmds
404453}
405454
406- // formatChatItem returns lines to print for a conversation item,
407- // matching the old Python console format.
408- func formatChatItem (item * agent.ChatContext_ChatItem ) []string {
455+ func formatChatItem (item * agent.ChatContext_ChatItem ) string {
409456 switch i := item .Item .(type ) {
410457 case * agent.ChatContext_ChatItem_Message :
411458 msg := i .Message
412- // User messages are printed from UserInputTranscribed (final) to avoid
413- // ordering issues with partial transcripts.
414459 if msg .Role == agent .ChatRole_USER {
415- return nil
460+ return ""
416461 }
417462 var textParts []string
418463 for _ , c := range msg .Content {
@@ -422,41 +467,30 @@ func formatChatItem(item *agent.ChatContext_ChatItem) []string {
422467 }
423468 text := strings .Join (textParts , "" )
424469 if text == "" {
425- return nil
426- }
427-
428- var lines []string
429- lines = append (lines ,
430- "\n " + lipgloss .NewStyle ().Foreground (lkGreen ).Render ("● " )+
431- greenBoldStyle .Render ("Agent" ),
432- )
433- parts := strings .Split (text , "\n " )
434- for i , tl := range parts {
435- if i == len (parts )- 1 {
436- lines = append (lines , " " + tl + "\n " )
437- } else {
438- lines = append (lines , " " + tl )
439- }
470+ return ""
440471 }
441- return lines
442472
443- case * agent.ChatContext_ChatItem_FunctionCall :
444- return []string {
445- " " + lipgloss .NewStyle ().Foreground (lkCyan ).Render ("➜ " ) +
446- cyanBoldStyle .Render (i .FunctionCall .Name ),
473+ var b strings.Builder
474+ b .WriteString ("\n " )
475+ b .WriteString (lipgloss .NewStyle ().Foreground (lkGreen ).Render ("● " ))
476+ b .WriteString (greenBoldStyle .Render ("Agent" ))
477+ for _ , tl := range strings .Split (text , "\n " ) {
478+ b .WriteString ("\n " )
479+ b .WriteString (tl )
447480 }
481+ b .WriteString ("\n " )
482+ return b .String ()
448483
449- case * agent.ChatContext_ChatItem_FunctionCallOutput :
450- if i .FunctionCallOutput .IsError {
451- return []string {
452- " " + redBoldStyle .Render ("✗ " ) + redStyle .Render (truncateOutput (i .FunctionCallOutput .Output )),
453- }
454- }
455- return []string {
456- " " + greenStyle .Render ("✓ " ) + dimStyle .Render (summarizeOutput (i .FunctionCallOutput .Output )),
484+ case * agent.ChatContext_ChatItem_AgentHandoff :
485+ h := i .AgentHandoff
486+ old := ""
487+ if h .OldAgentId != nil && * h .OldAgentId != "" {
488+ old = dimStyle .Render (* h .OldAgentId ) + " → "
457489 }
490+ return " " + lipgloss .NewStyle ().Foreground (lkPurple ).Render ("● " ) +
491+ dimStyle .Render ("handoff: " ) + old + labelStyle .Render (h .NewAgentId )
458492 }
459- return nil
493+ return ""
460494}
461495
462496// ──────────────────────────────────────────────────────────────────
0 commit comments