- {/* Circle wrapper — holds flash overlay */}
+ {/* Circle wrapper - holds flash overlay */}
{/* Green flash burst on completion */}
{isJustCompleted && isDone && (
@@ -96,7 +107,7 @@ export const StepProgressBar = ({
style={
isJustCompleted && isDone
? { animation: 'stepper-jelly 0.45s ease-out' }
- : isCurrent
+ : isAnimatingCurrent
? { animation: 'stepper-pulse-ring 2s ease-in-out infinite' }
: undefined
}
diff --git a/src/renderer/components/team/members/MemberCard.tsx b/src/renderer/components/team/members/MemberCard.tsx
index 19d99dfa..66a1a8ab 100644
--- a/src/renderer/components/team/members/MemberCard.tsx
+++ b/src/renderer/components/team/members/MemberCard.tsx
@@ -8,6 +8,7 @@ import { useTheme } from '@renderer/hooks/useTheme';
import { useStore } from '@renderer/store';
import { selectResolvedMembersForTeamName } from '@renderer/store/slices/teamSlice';
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
+import { renderLinkifiedText } from '@renderer/utils/linkifiedText';
import {
agentAvatarUrl,
buildMemberAvatarMap,
@@ -91,6 +92,34 @@ function splitRuntimeSummaryMemory(runtimeSummary: string | undefined): {
};
}
+function normalizeLaunchFailureReason(value: string | undefined): string | null {
+ const normalized = value
+ ?.replace(/\s+/g, ' ')
+ .trim()
+ .replace(/^Latest assistant message\s+\S+\s+failed with APIError\s*[-:]\s*/i, '')
+ .replace(/^APIError\s*[-:]\s*/i, '');
+ return normalized && normalized.length > 0 ? normalized : null;
+}
+
+function truncateLaunchFailureReason(value: string, maxLength = 220): string {
+ if (value.length <= maxLength) {
+ return value;
+ }
+ return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
+}
+
+function getLaunchFailureLinkLabel(url: string): string {
+ try {
+ const parsed = new URL(url);
+ if (parsed.hostname === 'openrouter.ai' && parsed.pathname === '/settings/credits') {
+ return 'OpenRouter credits';
+ }
+ } catch {
+ return url;
+ }
+ return url;
+}
+
export const MemberCard = memo(function MemberCard({
member,
memberColor,
@@ -244,6 +273,17 @@ export const MemberCard = memo(function MemberCard({
spawnEntry?.skippedForLaunch === true;
const showFailedLaunchBadge = !isRemoved && isFailedLaunch;
const showSkippedLaunchBadge = !isRemoved && isSkippedLaunch;
+ const rawLaunchFailureReason =
+ spawnError ??
+ spawnEntry?.hardFailureReason ??
+ spawnEntry?.runtimeDiagnostic ??
+ spawnEntry?.error;
+ const launchFailureReason = showFailedLaunchBadge
+ ? normalizeLaunchFailureReason(rawLaunchFailureReason)
+ : null;
+ const displayedLaunchFailureReason = launchFailureReason
+ ? truncateLaunchFailureReason(launchFailureReason)
+ : null;
const hasLiveLaunchControls =
isTeamAlive === true || isTeamProvisioning === true || isLaunchSettling === true;
const hasRestartMemberControl =
@@ -451,6 +491,21 @@ export const MemberCard = memo(function MemberCard({
) : null}
) : null}
+ {displayedLaunchFailureReason ? (
+
+
+ {renderLinkifiedText(displayedLaunchFailureReason, {
+ linkClassName: 'underline underline-offset-2 hover:text-red-200',
+ stopPropagation: true,
+ getLinkLabel: getLaunchFailureLinkLabel,
+ })}
+
+
+ ) : null}