Kaynağa Gözat

Feat: Add Agent Name in the LLM Response Footer (and re-order it) (#1770)

spoons-and-mirrors 6 ay önce
ebeveyn
işleme
bd4319f2bc

+ 30 - 2
packages/tui/internal/components/chat/message.go

@@ -324,9 +324,37 @@ func renderText(
 	if time.Now().Format("02 Jan 2006") == timestamp[:11] {
 		timestamp = timestamp[12:]
 	}
-	info := fmt.Sprintf("%s (%s)", author, timestamp)
-	info = styles.NewStyle().Foreground(t.TextMuted()).Render(info)
 
+	// Check if this is an assistant message with mode (agent) information
+	var modelAndAgentSuffix string
+	if assistantMsg, ok := message.(opencode.AssistantMessage); ok && assistantMsg.Mode != "" {
+		// Find the agent index by name to get the correct color
+		var agentIndex int
+		for i, agent := range app.Agents {
+			if agent.Name == assistantMsg.Mode {
+				agentIndex = i
+				break
+			}
+		}
+
+		// Get agent color based on the original agent index (same as status bar)
+		agentColor := util.GetAgentColor(agentIndex)
+
+		// Style the agent name with the same color as status bar
+		agentName := strings.Title(assistantMsg.Mode)
+		styledAgentName := styles.NewStyle().Foreground(agentColor).Bold(true).Render(agentName)
+		modelAndAgentSuffix = fmt.Sprintf(" | %s | %s", assistantMsg.ModelID, styledAgentName)
+	}
+
+	var info string
+	if modelAndAgentSuffix != "" {
+		// For assistant messages: "timestamp | modelID | agentName"
+		info = fmt.Sprintf("%s%s", timestamp, modelAndAgentSuffix)
+	} else {
+		// For user messages: "author (timestamp)"
+		info = fmt.Sprintf("%s (%s)", author, timestamp)
+	}
+	info = styles.NewStyle().Foreground(t.TextMuted()).Render(info)
 	if !showToolDetails && toolCalls != nil && len(toolCalls) > 0 {
 		content = content + "\n\n"
 		for _, toolCall := range toolCalls {

+ 7 - 23
packages/tui/internal/components/status/status.go

@@ -121,30 +121,14 @@ func (m *statusComponent) View() string {
 
 	var modeBackground compat.AdaptiveColor
 	var modeForeground compat.AdaptiveColor
-	switch m.app.AgentIndex {
-	case 0:
+
+	agentColor := util.GetAgentColor(m.app.AgentIndex)
+
+	if m.app.AgentIndex == 0 {
 		modeBackground = t.BackgroundElement()
-		modeForeground = t.TextMuted()
-	case 1:
-		modeBackground = t.Secondary()
-		modeForeground = t.BackgroundPanel()
-	case 2:
-		modeBackground = t.Accent()
-		modeForeground = t.BackgroundPanel()
-	case 3:
-		modeBackground = t.Success()
-		modeForeground = t.BackgroundPanel()
-	case 4:
-		modeBackground = t.Warning()
-		modeForeground = t.BackgroundPanel()
-	case 5:
-		modeBackground = t.Primary()
-		modeForeground = t.BackgroundPanel()
-	case 6:
-		modeBackground = t.Error()
-		modeForeground = t.BackgroundPanel()
-	default:
-		modeBackground = t.Secondary()
+		modeForeground = agentColor
+	} else {
+		modeBackground = agentColor
 		modeForeground = t.BackgroundPanel()
 	}
 

+ 23 - 1
packages/tui/internal/util/color.go

@@ -3,6 +3,9 @@ package util
 import (
 	"regexp"
 	"strings"
+
+	"github.com/charmbracelet/lipgloss/v2/compat"
+	"github.com/sst/opencode/internal/theme"
 )
 
 var csiRE *regexp.Regexp
@@ -89,5 +92,24 @@ func ConvertRGBToAnsi16Colors(s string) string {
 
 // func looksLikeByte(tok string) bool {
 // 	v, err := strconv.Atoi(tok)
-// 	return err == nil && v >= 0 && v <= 255
+//   return err == nil && v >= 0 && v <= 255
 // }
+
+// GetAgentColor returns the color for a given agent index, matching the status bar colors
+func GetAgentColor(agentIndex int) compat.AdaptiveColor {
+	t := theme.CurrentTheme()
+	agentColors := []compat.AdaptiveColor{
+		t.TextMuted(),
+		t.Secondary(),
+		t.Accent(),
+		t.Success(),
+		t.Warning(),
+		t.Primary(),
+		t.Error(),
+	}
+
+	if agentIndex >= 0 && agentIndex < len(agentColors) {
+		return agentColors[agentIndex]
+	}
+	return t.Secondary() // default fallback
+}

+ 21 - 5
packages/web/src/components/share/part.tsx

@@ -144,7 +144,15 @@ export function Part(props: PartProps) {
                   DateTime.DATETIME_FULL_WITH_SECONDS,
                 )}
               >
-                {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)}
+                {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString(
+                  DateTime.DATETIME_MED,
+                )}
+                {` | ${props.message.modelID}`}
+                {props.message.mode && (
+                  <span style={{ "font-weight": "bold", color: "var(--sl-color-accent)" }}>
+                    {` | ${props.message.mode}`}
+                  </span>
+                )}
               </Footer>
             )}
           </div>
@@ -158,7 +166,17 @@ export function Part(props: PartProps) {
         {props.part.type === "step-start" && props.message.role === "assistant" && (
           <div data-component="step-start">
             <div data-slot="provider">{props.message.providerID}</div>
-            <div data-slot="model">{props.message.modelID}</div>
+            <div data-slot="model">
+              {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString(
+                DateTime.DATETIME_MED,
+              )}
+              {` | ${props.message.modelID}`}
+              {props.message.mode && (
+                <span style={{ "font-weight": "bold", color: "var(--sl-color-accent)" }}>
+                  {` | ${props.message.mode}`}
+                </span>
+              )}
+            </div>
           </div>
         )}
         {props.part.type === "tool" && props.part.state.status === "error" && (
@@ -653,9 +671,7 @@ function TaskTool(props: ToolProps) {
         <span data-slot="name">Task</span>
         <span data-slot="target">{props.state.input.description}</span>
       </div>
-      <div data-component="tool-input">
-        &ldquo;{props.state.input.prompt}&rdquo;
-      </div>
+      <div data-component="tool-input">&ldquo;{props.state.input.prompt}&rdquo;</div>
       <ResultsButton showCopy="Show output" hideCopy="Hide output">
         <div data-component="tool-output">
           <ContentMarkdown expand text={props.state.output} />