diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index d862e528..6db5509d 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -1,6 +1,7 @@ package chat import ( + "encoding/json" "fmt" "path/filepath" "slices" @@ -252,6 +253,7 @@ func renderToolInvocation( metadata client.MessageInfo_Metadata_Tool_AdditionalProperties, showDetails bool, isLast bool, + contentOnly bool, ) string { ignoredTools := []string{"todoread"} if slices.Contains(ignoredTools, toolCall.ToolName) { @@ -313,7 +315,6 @@ func renderToolInvocation( keys = append(keys, key) } slices.Sort(keys) - firstKey := "" if len(keys) > 0 { firstKey = keys[0] @@ -454,14 +455,60 @@ func renderToolInvocation( body = toMarkdown(body, innerWidth, t.BackgroundSubtle()) body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1)) } + case "task": + if description, ok := toolArgsMap["description"].(string); ok { + title = fmt.Sprintf("TASK %s %s", description, elapsed) + if summary, ok := metadata.Get("summary"); ok { + toolcalls := summary.([]any) + // toolcalls := + + steps := []string{} + for _, toolcall := range toolcalls { + call := toolcall.(map[string]any) + if toolInvocation, ok := call["toolInvocation"].(map[string]any); ok { + data, _ := json.Marshal(toolInvocation) + var toolCall client.MessageToolInvocationToolCall + _ = json.Unmarshal(data, &toolCall) + + if metadata, ok := call["metadata"].(map[string]any); ok { + data, _ = json.Marshal(metadata) + var toolMetadata client.MessageInfo_Metadata_Tool_AdditionalProperties + _ = json.Unmarshal(data, &toolMetadata) + + step := renderToolInvocation( + toolCall, + nil, + toolMetadata, + false, + false, + true, + ) + steps = append(steps, step) + } + } + } + body = strings.Join(steps, "\n") + body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1)) + } + } + default: toolName := renderToolName(toolCall.ToolName) title = fmt.Sprintf("%s %s %s", toolName, toolArgs, elapsed) + if result == nil { + empty := "" + result = &empty + } body = *result body = truncateHeight(body, 10) body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1)) } + if contentOnly { + title = "∟ " + title + return title + } + if !showDetails { title = "∟ " + title padding := calculatePadding() @@ -502,8 +549,6 @@ func renderToolInvocation( func renderToolName(name string) string { switch name { - // case agent.AgentToolName: - // return "Task" case "list": return "LIST" case "webfetch": @@ -563,8 +608,8 @@ func renderFile(filename string, content string, options ...fileRenderingOption) func renderToolAction(name string) string { switch name { - // case agent.AgentToolName: - // return "Preparing prompt..." + case "task": + return "Searching..." case "bash": return "Building command..." case "edit": diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index c05bd27c..cee8aea8 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -191,6 +191,7 @@ func (m *messagesComponent) renderView() { metadata, m.showToolDetails, isLastToolInvocation, + false, ) m.cache.Set(key, content) } @@ -202,6 +203,7 @@ func (m *messagesComponent) renderView() { metadata, m.showToolDetails, isLastToolInvocation, + false, ) } diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 7dc0737e..0ce22a8e 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -212,6 +212,11 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case dialog.CompletionDialogCloseMsg: a.showCompletionDialog = false a.completions.SetProvider(a.completionManager.DefaultProvider()) + case client.EventInstallationUpdated: + return a, toast.NewSuccessToast( + "New version installed", + toast.WithTitle("opencode updated to "+msg.Properties.Version+", restart to apply."), + ) case client.EventSessionUpdated: if msg.Properties.Info.Id == a.app.Session.Id { a.app.Session = &msg.Properties.Info diff --git a/packages/tui/pkg/client/gen/openapi.json b/packages/tui/pkg/client/gen/openapi.json index 862a84d7..9ec1223d 100644 --- a/packages/tui/pkg/client/gen/openapi.json +++ b/packages/tui/pkg/client/gen/openapi.json @@ -525,6 +525,9 @@ { "$ref": "#/components/schemas/Event.storage.write" }, + { + "$ref": "#/components/schemas/Event.installation.updated" + }, { "$ref": "#/components/schemas/Event.lsp.client.diagnostics" }, @@ -537,9 +540,6 @@ { "$ref": "#/components/schemas/Event.message.part.updated" }, - { - "$ref": "#/components/schemas/Event.installation.updated" - }, { "$ref": "#/components/schemas/Event.session.updated" }, @@ -551,11 +551,11 @@ "propertyName": "type", "mapping": { "storage.write": "#/components/schemas/Event.storage.write", + "installation.updated": "#/components/schemas/Event.installation.updated", "lsp.client.diagnostics": "#/components/schemas/Event.lsp.client.diagnostics", "permission.updated": "#/components/schemas/Event.permission.updated", "message.updated": "#/components/schemas/Event.message.updated", "message.part.updated": "#/components/schemas/Event.message.part.updated", - "installation.updated": "#/components/schemas/Event.installation.updated", "session.updated": "#/components/schemas/Event.session.updated", "session.error": "#/components/schemas/Event.session.error" } @@ -586,6 +586,30 @@ "properties" ] }, + "Event.installation.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "installation.updated" + }, + "properties": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + }, + "required": [ + "version" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, "Event.lsp.client.diagnostics": { "type": "object", "properties": { @@ -1201,30 +1225,6 @@ "properties" ] }, - "Event.installation.updated": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "installation.updated" - }, - "properties": { - "type": "object", - "properties": { - "version": { - "type": "string" - } - }, - "required": [ - "version" - ] - } - }, - "required": [ - "type", - "properties" - ] - }, "Event.session.updated": { "type": "object", "properties": { @@ -1391,13 +1391,16 @@ "type": "object", "properties": { "$schema": { - "type": "string" + "type": "string", + "description": "JSON schema reference for configuration validation" }, "theme": { - "type": "string" + "type": "string", + "description": "Theme name to use for the interface" }, "keybinds": { - "$ref": "#/components/schemas/Config.Keybinds" + "$ref": "#/components/schemas/Config.Keybinds", + "description": "Custom keybind configurations" }, "autoshare": { "type": "boolean", @@ -1508,7 +1511,8 @@ "required": [ "models" ] - } + }, + "description": "Custom provider configurations and model overrides" }, "mcp": { "type": "object", @@ -1528,7 +1532,8 @@ "remote": "#/components/schemas/Config.McpRemote" } } - } + }, + "description": "MCP (Model Context Protocol) server configurations" } } }, @@ -1536,85 +1541,112 @@ "type": "object", "properties": { "leader": { - "type": "string" + "type": "string", + "description": "Leader key for keybind combinations" }, "help": { - "type": "string" + "type": "string", + "description": "Show help dialog" }, "editor_open": { - "type": "string" + "type": "string", + "description": "Open external editor" }, "session_new": { - "type": "string" + "type": "string", + "description": "Create a new session" }, "session_list": { - "type": "string" + "type": "string", + "description": "List all sessions" }, "session_share": { - "type": "string" + "type": "string", + "description": "Share current session" }, "session_interrupt": { - "type": "string" + "type": "string", + "description": "Interrupt current session" }, "session_compact": { - "type": "string" + "type": "string", + "description": "Toggle compact mode for session" }, "tool_details": { - "type": "string" + "type": "string", + "description": "Show tool details" }, "model_list": { - "type": "string" + "type": "string", + "description": "List available models" }, "theme_list": { - "type": "string" + "type": "string", + "description": "List available themes" }, "project_init": { - "type": "string" + "type": "string", + "description": "Initialize project configuration" }, "input_clear": { - "type": "string" + "type": "string", + "description": "Clear input field" }, "input_paste": { - "type": "string" + "type": "string", + "description": "Paste from clipboard" }, "input_submit": { - "type": "string" + "type": "string", + "description": "Submit input" }, "input_newline": { - "type": "string" + "type": "string", + "description": "Insert newline in input" }, "history_previous": { - "type": "string" + "type": "string", + "description": "Navigate to previous history item" }, "history_next": { - "type": "string" + "type": "string", + "description": "Navigate to next history item" }, "messages_page_up": { - "type": "string" + "type": "string", + "description": "Scroll messages up by one page" }, "messages_page_down": { - "type": "string" + "type": "string", + "description": "Scroll messages down by one page" }, "messages_half_page_up": { - "type": "string" + "type": "string", + "description": "Scroll messages up by half page" }, "messages_half_page_down": { - "type": "string" + "type": "string", + "description": "Scroll messages down by half page" }, "messages_previous": { - "type": "string" + "type": "string", + "description": "Navigate to previous message" }, "messages_next": { - "type": "string" + "type": "string", + "description": "Navigate to next message" }, "messages_first": { - "type": "string" + "type": "string", + "description": "Navigate to first message" }, "messages_last": { - "type": "string" + "type": "string", + "description": "Navigate to last message" }, "app_exit": { - "type": "string" + "type": "string", + "description": "Exit the application" } } }, @@ -1723,19 +1755,22 @@ "properties": { "type": { "type": "string", - "const": "local" + "const": "local", + "description": "Type of MCP server connection" }, "command": { "type": "array", "items": { "type": "string" - } + }, + "description": "Command and arguments to run the MCP server" }, "environment": { "type": "object", "additionalProperties": { "type": "string" - } + }, + "description": "Environment variables to set when running the MCP server" } }, "required": [ @@ -1748,10 +1783,12 @@ "properties": { "type": { "type": "string", - "const": "remote" + "const": "remote", + "description": "Type of MCP server connection" }, "url": { - "type": "string" + "type": "string", + "description": "URL of the remote MCP server" } }, "required": [ diff --git a/packages/tui/pkg/client/generated-client.go b/packages/tui/pkg/client/generated-client.go index 15acb548..a108357d 100644 --- a/packages/tui/pkg/client/generated-client.go +++ b/packages/tui/pkg/client/generated-client.go @@ -41,6 +41,7 @@ type AppInfo struct { // ConfigInfo defines model for Config.Info. type ConfigInfo struct { + // Schema JSON schema reference for configuration validation Schema *string `json:"$schema,omitempty"` // Autoshare Share newly created sessions automatically @@ -50,12 +51,16 @@ type ConfigInfo struct { Autoupdate *bool `json:"autoupdate,omitempty"` // DisabledProviders Disable providers that are loaded automatically - DisabledProviders *[]string `json:"disabled_providers,omitempty"` - Keybinds *ConfigKeybinds `json:"keybinds,omitempty"` - Mcp *map[string]ConfigInfo_Mcp_AdditionalProperties `json:"mcp,omitempty"` + DisabledProviders *[]string `json:"disabled_providers,omitempty"` + Keybinds *ConfigKeybinds `json:"keybinds,omitempty"` + + // Mcp MCP (Model Context Protocol) server configurations + Mcp *map[string]ConfigInfo_Mcp_AdditionalProperties `json:"mcp,omitempty"` // Model Model to use in the format of provider/model, eg anthropic/claude-2 - Model *string `json:"model,omitempty"` + Model *string `json:"model,omitempty"` + + // Provider Custom provider configurations and model overrides Provider *map[string]struct { Api *string `json:"api,omitempty"` Env *[]string `json:"env,omitempty"` @@ -81,6 +86,8 @@ type ConfigInfo struct { Npm *string `json:"npm,omitempty"` Options *map[string]interface{} `json:"options,omitempty"` } `json:"provider,omitempty"` + + // Theme Theme name to use for the interface Theme *string `json:"theme,omitempty"` } @@ -91,46 +98,107 @@ type ConfigInfo_Mcp_AdditionalProperties struct { // ConfigKeybinds defines model for Config.Keybinds. type ConfigKeybinds struct { - AppExit *string `json:"app_exit,omitempty"` - EditorOpen *string `json:"editor_open,omitempty"` - Help *string `json:"help,omitempty"` - HistoryNext *string `json:"history_next,omitempty"` - HistoryPrevious *string `json:"history_previous,omitempty"` - InputClear *string `json:"input_clear,omitempty"` - InputNewline *string `json:"input_newline,omitempty"` - InputPaste *string `json:"input_paste,omitempty"` - InputSubmit *string `json:"input_submit,omitempty"` - Leader *string `json:"leader,omitempty"` - MessagesFirst *string `json:"messages_first,omitempty"` + // AppExit Exit the application + AppExit *string `json:"app_exit,omitempty"` + + // EditorOpen Open external editor + EditorOpen *string `json:"editor_open,omitempty"` + + // Help Show help dialog + Help *string `json:"help,omitempty"` + + // HistoryNext Navigate to next history item + HistoryNext *string `json:"history_next,omitempty"` + + // HistoryPrevious Navigate to previous history item + HistoryPrevious *string `json:"history_previous,omitempty"` + + // InputClear Clear input field + InputClear *string `json:"input_clear,omitempty"` + + // InputNewline Insert newline in input + InputNewline *string `json:"input_newline,omitempty"` + + // InputPaste Paste from clipboard + InputPaste *string `json:"input_paste,omitempty"` + + // InputSubmit Submit input + InputSubmit *string `json:"input_submit,omitempty"` + + // Leader Leader key for keybind combinations + Leader *string `json:"leader,omitempty"` + + // MessagesFirst Navigate to first message + MessagesFirst *string `json:"messages_first,omitempty"` + + // MessagesHalfPageDown Scroll messages down by half page MessagesHalfPageDown *string `json:"messages_half_page_down,omitempty"` - MessagesHalfPageUp *string `json:"messages_half_page_up,omitempty"` - MessagesLast *string `json:"messages_last,omitempty"` - MessagesNext *string `json:"messages_next,omitempty"` - MessagesPageDown *string `json:"messages_page_down,omitempty"` - MessagesPageUp *string `json:"messages_page_up,omitempty"` - MessagesPrevious *string `json:"messages_previous,omitempty"` - ModelList *string `json:"model_list,omitempty"` - ProjectInit *string `json:"project_init,omitempty"` - SessionCompact *string `json:"session_compact,omitempty"` - SessionInterrupt *string `json:"session_interrupt,omitempty"` - SessionList *string `json:"session_list,omitempty"` - SessionNew *string `json:"session_new,omitempty"` - SessionShare *string `json:"session_share,omitempty"` - ThemeList *string `json:"theme_list,omitempty"` - ToolDetails *string `json:"tool_details,omitempty"` + + // MessagesHalfPageUp Scroll messages up by half page + MessagesHalfPageUp *string `json:"messages_half_page_up,omitempty"` + + // MessagesLast Navigate to last message + MessagesLast *string `json:"messages_last,omitempty"` + + // MessagesNext Navigate to next message + MessagesNext *string `json:"messages_next,omitempty"` + + // MessagesPageDown Scroll messages down by one page + MessagesPageDown *string `json:"messages_page_down,omitempty"` + + // MessagesPageUp Scroll messages up by one page + MessagesPageUp *string `json:"messages_page_up,omitempty"` + + // MessagesPrevious Navigate to previous message + MessagesPrevious *string `json:"messages_previous,omitempty"` + + // ModelList List available models + ModelList *string `json:"model_list,omitempty"` + + // ProjectInit Initialize project configuration + ProjectInit *string `json:"project_init,omitempty"` + + // SessionCompact Toggle compact mode for session + SessionCompact *string `json:"session_compact,omitempty"` + + // SessionInterrupt Interrupt current session + SessionInterrupt *string `json:"session_interrupt,omitempty"` + + // SessionList List all sessions + SessionList *string `json:"session_list,omitempty"` + + // SessionNew Create a new session + SessionNew *string `json:"session_new,omitempty"` + + // SessionShare Share current session + SessionShare *string `json:"session_share,omitempty"` + + // ThemeList List available themes + ThemeList *string `json:"theme_list,omitempty"` + + // ToolDetails Show tool details + ToolDetails *string `json:"tool_details,omitempty"` } // ConfigMcpLocal defines model for Config.McpLocal. type ConfigMcpLocal struct { - Command []string `json:"command"` + // Command Command and arguments to run the MCP server + Command []string `json:"command"` + + // Environment Environment variables to set when running the MCP server Environment *map[string]string `json:"environment,omitempty"` - Type string `json:"type"` + + // Type Type of MCP server connection + Type string `json:"type"` } // ConfigMcpRemote defines model for Config.McpRemote. type ConfigMcpRemote struct { + // Type Type of MCP server connection Type string `json:"type"` - Url string `json:"url"` + + // Url URL of the remote MCP server + Url string `json:"url"` } // Error defines model for Error. @@ -684,6 +752,34 @@ func (t *Event) MergeEventStorageWrite(v EventStorageWrite) error { return err } +// AsEventInstallationUpdated returns the union data inside the Event as a EventInstallationUpdated +func (t Event) AsEventInstallationUpdated() (EventInstallationUpdated, error) { + var body EventInstallationUpdated + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromEventInstallationUpdated overwrites any union data inside the Event as the provided EventInstallationUpdated +func (t *Event) FromEventInstallationUpdated(v EventInstallationUpdated) error { + v.Type = "installation.updated" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeEventInstallationUpdated performs a merge with any union data inside the Event, using the provided EventInstallationUpdated +func (t *Event) MergeEventInstallationUpdated(v EventInstallationUpdated) error { + v.Type = "installation.updated" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + // AsEventLspClientDiagnostics returns the union data inside the Event as a EventLspClientDiagnostics func (t Event) AsEventLspClientDiagnostics() (EventLspClientDiagnostics, error) { var body EventLspClientDiagnostics @@ -796,34 +892,6 @@ func (t *Event) MergeEventMessagePartUpdated(v EventMessagePartUpdated) error { return err } -// AsEventInstallationUpdated returns the union data inside the Event as a EventInstallationUpdated -func (t Event) AsEventInstallationUpdated() (EventInstallationUpdated, error) { - var body EventInstallationUpdated - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromEventInstallationUpdated overwrites any union data inside the Event as the provided EventInstallationUpdated -func (t *Event) FromEventInstallationUpdated(v EventInstallationUpdated) error { - v.Type = "installation.updated" - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeEventInstallationUpdated performs a merge with any union data inside the Event, using the provided EventInstallationUpdated -func (t *Event) MergeEventInstallationUpdated(v EventInstallationUpdated) error { - v.Type = "installation.updated" - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - // AsEventSessionUpdated returns the union data inside the Event as a EventSessionUpdated func (t Event) AsEventSessionUpdated() (EventSessionUpdated, error) { var body EventSessionUpdated