-
Notifications
You must be signed in to change notification settings - Fork 0
feat(tui): improve tool event handling with fallback matching and upd… #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -677,6 +677,31 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |
| return m, tea.Batch(listenForEvents(m.eventCh), tickCmd()) | ||
| } | ||
| } | ||
| // No matching start entry by callID — try matching by tool name | ||
| // as a fallback for the most recent running entry of the same type. | ||
| toolName := getToolNameFromEventContent(msg.event.Content) | ||
| if toolName != "" { | ||
| if matchedID, matchedEntry := findRunningEntryByName(m.toolCallEntries, toolName); matchedEntry != nil { | ||
| resultContent := getResultFromEventContent(msg.event.Content) | ||
| isError := strings.HasPrefix(resultContent, "Error:") | ||
| matchedEntry.SetResult(tui.ToolResultInfo{ | ||
| ToolCallID: matchedID, | ||
| Name: matchedEntry.Call.Name, | ||
| Content: resultContent, | ||
| IsError: isError, | ||
| }) | ||
| if idx := findLogEntryByToolCallID(m.logEntries, matchedID); idx >= 0 { | ||
| le := &m.logEntries[idx] | ||
| le.content = resultContent | ||
| le.isToolRunning = false | ||
| le.rendered = "" | ||
| } | ||
| delete(m.toolCallEntries, matchedID) | ||
| m.updateActiveAnim() | ||
| m.buildViewportContent() | ||
| return m, tea.Batch(listenForEvents(m.eventCh), tickCmd()) | ||
| } | ||
| } | ||
| // No matching start entry — add as standalone | ||
| } | ||
|
|
||
|
|
@@ -1205,6 +1230,30 @@ func formatEventAsEntry(event *messaging.MessageEvent) logEntry { | |
| if entry.content == "" { | ||
| entry.content = fmt.Sprintf("%v", event.Content) | ||
| } | ||
| // Create a ToolEntry for standalone results so they use new-style | ||
| // rendering (✓ read_file · /path/to/file) instead of the legacy | ||
| // emoji path (✅ read_file). | ||
| isErr := strings.HasPrefix(entry.content, "Error:") | ||
| entry.toolEntry = tui.NewToolEntry(tui.ToolCallInfo{ | ||
| ID: entry.toolCallID, | ||
| Name: entry.toolName, | ||
| Summary: extractToolSummary(entry.toolName, ""), | ||
| }) | ||
| if isErr { | ||
| entry.toolEntry.SetResult(tui.ToolResultInfo{ | ||
| ToolCallID: entry.toolCallID, | ||
| Name: entry.toolName, | ||
| Content: entry.content, | ||
| IsError: true, | ||
| }) | ||
| } else { | ||
| entry.toolEntry.SetResult(tui.ToolResultInfo{ | ||
| ToolCallID: entry.toolCallID, | ||
| Name: entry.toolName, | ||
| Content: entry.content, | ||
| IsError: false, | ||
| }) | ||
| } | ||
| case "user_help_needed": | ||
| if s, ok := event.Content.(string); ok { | ||
| entry.content = "HELP: " + s | ||
|
|
@@ -1799,6 +1848,29 @@ func getToolCallIDFromEventContent(content interface{}) string { | |
| return "" | ||
| } | ||
|
|
||
| // getToolNameFromEventContent extracts tool_name from event content. | ||
| func getToolNameFromEventContent(content interface{}) string { | ||
| if m, ok := content.(map[string]interface{}); ok { | ||
| if name, ok := m["tool_name"]; ok { | ||
| if nameStr, ok := name.(string); ok { | ||
| return nameStr | ||
| } | ||
| } | ||
| } | ||
| return "" | ||
| } | ||
|
|
||
| // findRunningEntryByName finds the most recently-added running entry with the | ||
| // given tool name in the toolCallEntries map. Returns the call ID and the entry. | ||
| func findRunningEntryByName(entries map[string]*tui.ToolEntry, toolName string) (string, *tui.ToolEntry) { | ||
| for id, entry := range entries { | ||
|
Comment on lines
+1863
to
+1866
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): Iterating a map does not guarantee returning the most recently-added running entry, conflicting with the function’s documented behavior. Because Go map iteration order is random, |
||
| if entry.Call.Name == toolName && entry.Status == tui.ToolStatusRunning { | ||
| return id, entry | ||
| } | ||
| } | ||
| return "", nil | ||
| } | ||
|
|
||
| // getResultFromEventContent extracts the result string from event content. | ||
| func getResultFromEventContent(content interface{}) string { | ||
| if m, ok := content.(map[string]interface{}); ok { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: The
ToolResultInfoconstruction is duplicated and can be simplified using the existingisErrvariable.Since
isErris already computed, you can avoid duplicating theToolResultInfoliteral in both branches by constructing it once, settingIsError: isErr, and callingSetResulta single time. This removes repetition and makes future changes to the struct safer.