Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ interface SessionViewProps {
hasError?: boolean;
errorTitle?: string;
errorMessage?: string;
errorRetryable?: boolean;
onRetry?: () => void;
onNewSession?: () => void;
isInitializing?: boolean;
Expand All @@ -78,6 +79,48 @@ interface SessionViewProps {
const DEFAULT_ERROR_MESSAGE =
"Failed to resume this session. The working directory may have been deleted. Please start a new session.";

interface CloudStreamDisconnectedBannerProps {
errorTitle?: string;
errorMessage?: string;
onRetry?: () => void;
}

function CloudStreamDisconnectedBanner({
errorTitle,
errorMessage,
onRetry,
}: CloudStreamDisconnectedBannerProps) {
return (
<Flex
align="center"
justify="between"
gap="3"
py="2"
px="3"
className="shrink-0 border-(--red-5) border-b bg-(--red-2)"
>
<Flex align="center" gap="2" className="min-w-0">
<Warning size={14} weight="duotone" color="var(--red-9)" />
{errorTitle && (
<Text className="shrink-0 font-medium text-(--red-12) text-[13px]">
{errorTitle}
</Text>
)}
{errorMessage && (
<Text color="gray" className="truncate text-[13px]">
{errorMessage}
</Text>
)}
</Flex>
{onRetry && (
<Button variant="soft" size="1" color="red" onClick={onRetry}>
Retry
</Button>
)}
</Flex>
);
}

/**
* When an allow_always permission is granted outside a mode-switch prompt,
* ratchet the session to the closest "auto-accept edits" preset offered by
Expand Down Expand Up @@ -116,6 +159,7 @@ export function SessionView({
hasError = false,
errorTitle,
errorMessage = DEFAULT_ERROR_MESSAGE,
errorRetryable = false,
onRetry,
onNewSession,
isInitializing = false,
Expand All @@ -137,6 +181,7 @@ export function SessionView({
const currentModeId = modeOption?.currentValue;
const handoffInProgress =
useSessionForTask(taskId)?.handoffInProgress ?? false;
const showInlineBanner = hasError && errorRetryable && events.length > 0;

useEffect(() => {
if (allowBypassPermissions) return;
Expand Down Expand Up @@ -524,6 +569,13 @@ export function SessionView({
) : (
<>
<DropZoneOverlay isVisible={isDraggingFile} />
{showInlineBanner && (
<CloudStreamDisconnectedBanner
errorTitle={errorTitle}
errorMessage={errorMessage}
onRetry={onRetry}
/>
)}
<ConversationView
events={events}
isPromptPending={isPromptPending}
Expand All @@ -537,7 +589,7 @@ export function SessionView({

<PlanStatusBar plan={latestPlan} />

{hasError ? (
{hasError && !showInlineBanner ? (
<Flex
align="center"
justify="center"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ export function useSessionViewState(taskId: string, task: Task) {
errorMessage:
session?.errorMessage ??
(isCloud ? session?.cloudErrorMessage : undefined),
errorRetryable: session?.errorRetryable,
};
}
4 changes: 4 additions & 0 deletions apps/code/src/renderer/features/sessions/service/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3127,11 +3127,13 @@ export class SessionService {

const previousErrorTitle = session.errorTitle;
const previousErrorMessage = session.errorMessage;
const previousErrorRetryable = session.errorRetryable;

sessionStoreSetters.updateSession(session.taskRunId, {
status: "disconnected",
errorTitle: undefined,
errorMessage: undefined,
errorRetryable: undefined,
isPromptPending: false,
});

Expand All @@ -3145,6 +3147,7 @@ export class SessionService {
status: "error",
errorTitle: previousErrorTitle,
errorMessage: previousErrorMessage,
errorRetryable: previousErrorRetryable,
});
throw error;
}
Expand All @@ -3170,6 +3173,7 @@ export class SessionService {
errorMessage:
update.errorMessage ??
"Lost connection to the cloud run. Retry to reconnect.",
errorRetryable: update.retryable,
isPromptPending: false,
});
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface AgentSession {
status: "connecting" | "connected" | "disconnected" | "error";
errorTitle?: string;
errorMessage?: string;
errorRetryable?: boolean;
isPromptPending: boolean;
isCompacting: boolean;
promptStartedAt: number | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function TaskLogsPanel({ taskId, task, hideInput }: TaskLogsPanelProps) {
cloudStatus,
errorTitle,
errorMessage,
errorRetryable,
} = useSessionViewState(taskId, task);

useSessionConnection({
Expand Down Expand Up @@ -145,6 +146,7 @@ export function TaskLogsPanel({ taskId, task, hideInput }: TaskLogsPanelProps) {
hasError={hasError}
errorTitle={errorTitle}
errorMessage={errorMessage ?? undefined}
errorRetryable={errorRetryable}
hideInput={hideInput}
onRetry={handleRetry}
onNewSession={isCloud ? undefined : handleNewSession}
Expand Down
Loading