Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions pkg/runtime/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"cmp"

"github.com/docker/cagent/pkg/chat"
"github.com/docker/cagent/pkg/config/types"
"github.com/docker/cagent/pkg/tools"
)

Expand Down Expand Up @@ -392,10 +393,11 @@ func AgentInfo(agentName, model, description, welcomeMessage string) Event {

// AgentDetails contains information about an agent for display in the sidebar
type AgentDetails struct {
Name string `json:"name"`
Description string `json:"description"`
Provider string `json:"provider"`
Model string `json:"model"`
Name string `json:"name"`
Description string `json:"description"`
Provider string `json:"provider"`
Model string `json:"model"`
Commands types.Commands `json:"commands,omitempty"`
}

// TeamInfoEvent is sent when team information is available
Expand Down
1 change: 1 addition & 0 deletions pkg/runtime/remote_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func (r *RemoteRuntime) agentDetailsFromConfig(ctx context.Context) []AgentDetai
info := AgentDetails{
Name: agent.Name,
Description: agent.Description,
Commands: agent.Commands,
}

if provider, model, found := strings.Cut(agent.Model, "/"); found {
Expand Down
1 change: 1 addition & 0 deletions pkg/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ func (r *LocalRuntime) agentDetailsFromTeam() []AgentDetails {
Description: info.Description,
Provider: info.Provider,
Model: info.Model,
Commands: info.Commands,
}
}
return details
Expand Down
3 changes: 3 additions & 0 deletions pkg/team/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/docker/cagent/pkg/agent"
"github.com/docker/cagent/pkg/config/types"
"github.com/docker/cagent/pkg/permissions"
"github.com/docker/cagent/pkg/rag"
)
Expand Down Expand Up @@ -62,6 +63,7 @@ type AgentInfo struct {
Description string
Provider string
Model string
Commands types.Commands
}

// AgentsInfo returns information about all agents in the team
Expand All @@ -71,6 +73,7 @@ func (t *Team) AgentsInfo() []AgentInfo {
info := AgentInfo{
Name: a.Name(),
Description: a.Description(),
Commands: a.Commands(),
}
if model := a.Model(); model != nil {
modelID := model.ID()
Expand Down
8 changes: 4 additions & 4 deletions pkg/tui/components/messages/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ func (m *model) addMessage(msg *types.Message) tea.Cmd {

m.messages = append(m.messages, msg)
view := m.createMessageView(msg)
m.sessionState.PreviousMessage = msg
m.sessionState.SetPreviousMessage(msg)
m.views = append(m.views, view)

var cmds []tea.Cmd
Expand All @@ -854,7 +854,7 @@ func (m *model) LoadFromSession(sess *session.Session) tea.Cmd {
appendSessionMessage := func(msg *types.Message, view layout.Model) {
m.messages = append(m.messages, msg)
m.views = append(m.views, view)
m.sessionState.PreviousMessage = msg
m.sessionState.SetPreviousMessage(msg)
}

// getOrCreateReasoningBlock returns an existing reasoning block for the agent if the
Expand Down Expand Up @@ -1106,7 +1106,7 @@ func (m *model) addReasoningBlock(agentName, content string) tea.Cmd {

m.messages = append(m.messages, msg)
m.views = append(m.views, block)
m.sessionState.PreviousMessage = msg
m.sessionState.SetPreviousMessage(msg)

var cmds []tea.Cmd
if initCmd := block.Init(); initCmd != nil {
Expand Down Expand Up @@ -1164,7 +1164,7 @@ func (m *model) createToolCallView(msg *types.Message) layout.Model {
}

func (m *model) createMessageView(msg *types.Message) layout.Model {
view := message.New(msg, m.sessionState.PreviousMessage)
view := message.New(msg, m.sessionState.PreviousMessage())
view.SetSize(m.contentWidth(), 0)
return view
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/tui/components/sidebar/sidebar.go
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ func (m *model) queueSection(contentWidth int) string {
// agentInfo renders the current agent information
func (m *model) agentInfo(contentWidth int) string {
// Read current agent from session state so sidebar updates when agent is switched
currentAgent := m.sessionState.CurrentAgent
currentAgent := m.sessionState.CurrentAgentName()
if currentAgent == "" {
return ""
}
Expand Down Expand Up @@ -792,10 +792,10 @@ func (m *model) toolsetInfo(contentWidth int) string {
label string
shortcut string
}{
{m.sessionState.YoloMode, "YOLO mode enabled", "^y"},
{m.sessionState.Thinking && m.reasoningSupported, "Thinking enabled", "/think"},
{m.sessionState.HideToolResults, "Tool output hidden", "^o"},
{m.sessionState.SplitDiffView, "Split Diff View enabled", "^t"},
{m.sessionState.YoloMode(), "YOLO mode enabled", "^y"},
{m.sessionState.Thinking() && m.reasoningSupported, "Thinking enabled", "/think"},
{m.sessionState.HideToolResults(), "Tool output hidden", "^o"},
{m.sessionState.SplitDiffView(), "Split Diff View enabled", "^t"},
}

for _, toggle := range toggles {
Expand Down
4 changes: 2 additions & 2 deletions pkg/tui/components/tool/api/apitool.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func New(msg *types.Message, sessionState *service.SessionState) layout.Model {
func render(msg *types.Message, s spinner.Spinner, sessionState *service.SessionState, width, _ int) string {
var args map[string]any
if err := json.Unmarshal([]byte(msg.ToolCall.Function.Arguments), &args); err != nil {
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults())
}

// Extract argument summary for the tool call display
Expand All @@ -42,7 +42,7 @@ func render(msg *types.Message, s spinner.Spinner, sessionState *service.Session
params += styles.MutedStyle.Render(": Received " + units.HumanSize(float64(len(msg.Content))))
}

return toolcommon.RenderTool(msg, s, params, "", width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, params, "", width, sessionState.HideToolResults())
}

// extractEndpoint tries to find the endpoint/URL being called.
Expand Down
4 changes: 2 additions & 2 deletions pkg/tui/components/tool/defaulttool/defaulttool.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ func render(msg *types.Message, s spinner.Spinner, sessionState *service.Session
}

if argsContent == "" {
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults())
}

var resultContent string
if (msg.ToolStatus == types.ToolStatusCompleted || msg.ToolStatus == types.ToolStatusError) && msg.Content != "" {
resultContent = toolcommon.FormatToolResult(msg.Content, width)
}

return toolcommon.RenderTool(msg, s, argsContent, resultContent, width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, argsContent, resultContent, width, sessionState.HideToolResults())
}
4 changes: 2 additions & 2 deletions pkg/tui/components/tool/editfile/editfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ func render(msg *types.Message, s spinner.Spinner, sessionState *service.Session
styles.ToolName.Render(msg.ToolDefinition.DisplayName()),
styles.ToolMessageStyle.Render(toolcommon.ShortenPath(args.Path)))

if !sessionState.HideToolResults {
if !sessionState.HideToolResults() {
if msg.ToolCall.Function.Arguments != "" {
contentWidth := width - styles.ToolCallResult.GetHorizontalFrameSize()
content += "\n" + styles.ToolCallResult.Render(
renderEditFile(msg.ToolCall, contentWidth, sessionState.SplitDiffView, msg.ToolStatus))
renderEditFile(msg.ToolCall, contentWidth, sessionState.SplitDiffView(), msg.ToolStatus))
}

if (msg.ToolStatus == types.ToolStatusError) && msg.Content != "" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ func render(msg *types.Message, s spinner.Spinner, sessionState *service.Session
// Parse arguments
var args builtin.ReadMultipleFilesArgs
if err := json.Unmarshal([]byte(msg.ToolCall.Function.Arguments), &args); err != nil {
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults())
}

// For pending/running state, show files being read
if msg.ToolStatus == types.ToolStatusPending || msg.ToolStatus == types.ToolStatusRunning {
return toolcommon.RenderTool(msg, s, formatFilesList(args.Paths), "", width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, formatFilesList(args.Paths), "", width, sessionState.HideToolResults())
}

// For completed/error state, render each file line
Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/components/tool/todotool/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ func New(msg *types.Message, sessionState *service.SessionState) layout.Model {
}

func render(msg *types.Message, s spinner.Spinner, sessionState *service.SessionState, width, _ int) string {
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults)
return toolcommon.RenderTool(msg, s, "", "", width, sessionState.HideToolResults())
}
4 changes: 2 additions & 2 deletions pkg/tui/components/toolcommon/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func SimpleRenderer(extractArg func(args string) string) Renderer {
if msg.ToolCall.Function.Arguments != "" {
arg = extractArg(msg.ToolCall.Function.Arguments)
}
return RenderTool(msg, s, arg, "", width, sessionState.HideToolResults)
return RenderTool(msg, s, arg, "", width, sessionState.HideToolResults())
}
}

Expand All @@ -123,6 +123,6 @@ func SimpleRendererWithResult(
result = extractResult(msg)
}

return RenderTool(msg, s, arg, result, width, sessionState.HideToolResults)
return RenderTool(msg, s, arg, result, width, sessionState.HideToolResults())
}
}
5 changes: 1 addition & 4 deletions pkg/tui/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ func (a *appModel) handleNewSession() (tea.Model, tea.Cmd) {
a.application.NewSession()
sess := a.application.Session()
a.sessionState = service.NewSessionState(sess)
a.sessionTitle = ""
a.chatPage = chat.New(a.application, a.sessionState)
a.dialog = dialog.New()
a.statusBar.SetHelp(a.chatPage)
Expand Down Expand Up @@ -71,7 +70,6 @@ func (a *appModel) handleLoadSession(sessionID string) (tea.Model, tea.Cmd) {
// Cancel current session and replace with loaded one
a.application.ReplaceSession(context.Background(), sess)
a.sessionState = service.NewSessionState(sess)
a.sessionTitle = sess.Title
a.chatPage = chat.New(a.application, a.sessionState)
a.dialog = dialog.New()
a.statusBar.SetHelp(a.chatPage)
Expand Down Expand Up @@ -179,8 +177,7 @@ func (a *appModel) handleSwitchAgent(agentName string) (tea.Model, tea.Cmd) {
return a, notification.ErrorCmd(fmt.Sprintf("Failed to switch to agent '%s': %v", agentName, err))
}

a.currentAgent = agentName
a.sessionState.SetCurrentAgent(agentName)
a.sessionState.SetCurrentAgentName(agentName)
return a, notification.SuccessCmd(fmt.Sprintf("Switched to agent '%s'", agentName))
}

Expand Down
110 changes: 82 additions & 28 deletions pkg/tui/service/sessionstate.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package service

import (
"github.com/docker/cagent/pkg/runtime"
"github.com/docker/cagent/pkg/session"
"github.com/docker/cagent/pkg/tui/types"
)
Expand All @@ -9,48 +10,101 @@ import (
// This provides a centralized location for state that needs to be
// accessible by multiple components.
type SessionState struct {
// SplitDiffView determines whether diff views should be shown side-by-side (true)
// or unified (false)
SplitDiffView bool
YoloMode bool
Thinking bool // true = enabled (default), false = disabled
HideToolResults bool
PreviousMessage *types.Message
// CurrentAgent is the name of the currently active agent for user messages
CurrentAgent string
}

// NewSessionState creates a new SessionState with default values.
func NewSessionState(sessionState *session.Session) *SessionState {
splitDiffView bool
yoloMode bool
thinking bool
hideToolResults bool
sessionTitle string

previousMessage *types.Message
currentAgentName string
availableAgents []runtime.AgentDetails
}

func NewSessionState(s *session.Session) *SessionState {
return &SessionState{
SplitDiffView: true, // Default to split view
YoloMode: sessionState.ToolsApproved,
Thinking: sessionState.Thinking,
HideToolResults: sessionState.HideToolResults,
splitDiffView: true,
yoloMode: s.ToolsApproved,
thinking: s.Thinking,
hideToolResults: s.HideToolResults,
sessionTitle: s.Title,
}
}

// ToggleSplitDiffView toggles between split and unified diff view modes.
func (s *SessionState) SplitDiffView() bool {
return s.splitDiffView
}

func (s *SessionState) ToggleSplitDiffView() {
s.SplitDiffView = !s.SplitDiffView
s.splitDiffView = !s.splitDiffView
}

func (s *SessionState) YoloMode() bool {
return s.yoloMode
}

func (s *SessionState) SetYoloMode(enabled bool) {
s.YoloMode = enabled
func (s *SessionState) SetYoloMode(yoloMode bool) {
s.yoloMode = yoloMode
}

func (s *SessionState) SetHideToolResults(enabled bool) {
s.HideToolResults = enabled
func (s *SessionState) Thinking() bool {
return s.thinking
}

func (s *SessionState) SetThinking(thinking bool) {
s.thinking = thinking
}

func (s *SessionState) HideToolResults() bool {
return s.hideToolResults
}

func (s *SessionState) ToggleHideToolResults() {
s.HideToolResults = !s.HideToolResults
s.hideToolResults = !s.hideToolResults
}

func (s *SessionState) SetHideToolResults(hideToolResults bool) {
s.hideToolResults = hideToolResults
}

func (s *SessionState) CurrentAgentName() string {
return s.currentAgentName
}

func (s *SessionState) SetCurrentAgent(agentName string) {
s.CurrentAgent = agentName
func (s *SessionState) SetCurrentAgentName(currentAgentName string) {
s.currentAgentName = currentAgentName
}

func (s *SessionState) SetThinking(enabled bool) {
s.Thinking = enabled
func (s *SessionState) PreviousMessage() *types.Message {
return s.previousMessage
}

func (s *SessionState) SetPreviousMessage(previousMessage *types.Message) {
s.previousMessage = previousMessage
}

func (s *SessionState) SessionTitle() string {
return s.sessionTitle
}

func (s *SessionState) SetSessionTitle(sessionTitle string) {
s.sessionTitle = sessionTitle
}

func (s *SessionState) AvailableAgents() []runtime.AgentDetails {
return s.availableAgents
}

func (s *SessionState) SetAvailableAgents(availableAgents []runtime.AgentDetails) {
s.availableAgents = availableAgents
}

func (s *SessionState) GetCurrentAgent() runtime.AgentDetails {
for _, agent := range s.availableAgents {
if agent.Name == s.currentAgentName {
return agent
}
}

return runtime.AgentDetails{}
}
Loading