package channels import ( "errors" "strings" "github.com/nextlevelbuilder/goclaw/internal/providers" ) // isTransientFailureMessage reports whether an agent run failure string is a // transient provider error (rate limit, timeout, overload, connection drop) // that the user can simply retry — as opposed to a permanent error (auth, // context overflow). Used to decide whether a failure should surface a // terminal 💔 reaction (see events.go reaction forwarding). func isTransientFailureMessage(errStr string) bool { if errStr == "" { return false } lower := strings.ToLower(errStr) // Reuse the provider retryability classifier (429/5xx typed errors, // connection reset, timeouts, rate limits, EOF) on the raw message. if providers.IsRetryableError(errors.New(errStr)) { return true } // Mirror FormatAgentError's transient matchers (rate limit, timeout, // overload) plus HTTP 5xx status codes, which IsRetryableError only matches // on typed HTTPError values — the string seen here has been re-wrapped. return strings.Contains(lower, "rate limit") || strings.Contains(lower, "too many requests") || strings.Contains(lower, "429") || strings.Contains(lower, "timeout") || strings.Contains(lower, "deadline exceeded") || strings.Contains(lower, "overload") || strings.Contains(lower, "http 5") } // FormatAgentError converts internal error to user-friendly message. // Issue 958: Send user-friendly error on RunFailed instead of silent "...". func FormatAgentError(errStr string) string { if errStr == "" { return "" } lower := strings.ToLower(errStr) // Context overflow (highest priority — specific actionable message) if providers.IsContextOverflowMessage(lower) { return "⚠️ The conversation has grown too long. Please start a new chat or ask me to summarize." } // Rate limit if strings.Contains(lower, "rate limit") || strings.Contains(lower, "too many requests") || strings.Contains(lower, "429") { return "⏳ Too many requests. Please wait a moment and try again." } // Auth errors if strings.Contains(lower, "unauthorized") || strings.Contains(lower, "invalid api key") || strings.Contains(lower, "401") || strings.Contains(lower, "403") { return "🔑 Authentication error. Please check your API configuration." } // Timeout if strings.Contains(lower, "timeout") || strings.Contains(lower, "deadline exceeded") { return "⏱️ Request timed out. Please try again." } // Overloaded if strings.Contains(lower, "overload") { return "🔄 Service is busy. Please try again in a moment." } // Generic fallback (don't expose internal error details) return "❌ Something went wrong. Please try again." }