summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorspoons-and-mirrors <[email protected]>2025-08-10 03:22:16 +0200
committerGitHub <[email protected]>2025-08-09 20:22:16 -0500
commitbd4319f2bc28c0ad16946107e7d1d50fa924527e (patch)
treee1a404ee7e1aebe7f6a4b804a847b2530ca37de9
parent696ab1a752e5bfa520379e21d933be1cd3058ce0 (diff)
downloadopencode-bd4319f2bc28c0ad16946107e7d1d50fa924527e.tar.gz
opencode-bd4319f2bc28c0ad16946107e7d1d50fa924527e.zip
Feat: Add Agent Name in the LLM Response Footer (and re-order it) (#1770)
-rw-r--r--packages/tui/internal/components/chat/message.go32
-rw-r--r--packages/tui/internal/components/status/status.go30
-rw-r--r--packages/tui/internal/util/color.go24
-rw-r--r--packages/web/src/components/share/part.tsx26
4 files changed, 81 insertions, 31 deletions
diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go
index 6dcdcc989..5f4dc9612 100644
--- a/packages/tui/internal/components/chat/message.go
+++ b/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 {
diff --git a/packages/tui/internal/components/status/status.go b/packages/tui/internal/components/status/status.go
index be34dd938..1634bd6c5 100644
--- a/packages/tui/internal/components/status/status.go
+++ b/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()
}
diff --git a/packages/tui/internal/util/color.go b/packages/tui/internal/util/color.go
index f0d73bcb2..b387ca655 100644
--- a/packages/tui/internal/util/color.go
+++ b/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
+}
diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx
index 4a9320e6d..772a80dc6 100644
--- a/packages/web/src/components/share/part.tsx
+++ b/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} />