| 245 | } |
| 246 | |
| 247 | func formatToolCallResponse(response string) string { |
| 248 | if response == "" { |
| 249 | return " → ()" |
| 250 | } |
| 251 | |
| 252 | // For responses, we want to show them as readable text, not JSON |
| 253 | // Check if it looks like JSON first |
| 254 | var parsed any |
| 255 | if err := json.Unmarshal([]byte(response), &parsed); err == nil { |
| 256 | // It's valid JSON, format it nicely |
| 257 | return " → " + formatParsedJSON(parsed) |
| 258 | } |
| 259 | |
| 260 | // It's plain text, handle multiline content |
| 261 | if strings.Contains(response, "\n") { |
| 262 | // Trim whitespace and split into lines |
| 263 | trimmed := strings.TrimSpace(response) |
| 264 | lines := strings.Split(trimmed, "\n") |
| 265 | |
| 266 | if len(lines) <= 3 { |
| 267 | // Short multiline, show inline |
| 268 | return fmt.Sprintf(" → %q", response) |
| 269 | } |
| 270 | |
| 271 | // Long multiline, format with line breaks |
| 272 | // Process each line individually and collapse consecutive empty lines |
| 273 | var formatted []string |
| 274 | lastWasEmpty := false |
| 275 | |
| 276 | for _, line := range lines { |
| 277 | trimmedLine := strings.TrimSpace(line) |
| 278 | if trimmedLine == "" { |
| 279 | // Empty line - only add one if the last line wasn't empty |
| 280 | if !lastWasEmpty { |
| 281 | formatted = append(formatted, "") |
| 282 | lastWasEmpty = true |
| 283 | } |
| 284 | } else { |
| 285 | formatted = append(formatted, line) |
| 286 | lastWasEmpty = false |
| 287 | } |
| 288 | } |
| 289 | return fmt.Sprintf(" → (\n%s\n)", strings.Join(formatted, "\n")) |
| 290 | } |
| 291 | |
| 292 | // Single line text response |
| 293 | return fmt.Sprintf(" → %q", response) |
| 294 | } |
| 295 | |
| 296 | func formatParsedJSON(data any) string { |
| 297 | switch v := data.(type) { |