"use client"; import { IconAlertCircle, IconCheck, IconCircleCheck, IconLoader2, IconMessageQuestion, IconPlayerPause, IconShieldQuestion, IconX, } from "@tabler/icons-react"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { AgentLogo } from "@/components/agent-logo"; import { useAppStore } from "@/components/state-provider"; import { GridSpinner } from "@/components/grid-spinner"; import { MobilePickerSheet } from "@/components/task/mobile/mobile-picker-sheet"; import { MobilePillButton } from "@/components/task/mobile/mobile-pill-button"; import { SessionTabs, type SessionTab } from "@/components/session-tabs"; import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint"; import type { AgentProfileOption } from "@/lib/state/slices"; import type { TaskSession } from "@/lib/types/http"; import { isSessionActive, sortSessions } from "@/components/task/session-sort"; import { resolveThreadSessionStatus, type ThreadStatus } from "@/lib/threads/thread-session-status"; export type ThreadSessionView = { session: TaskSession; label: string; agentName: string | null; isPrimary: boolean; }; const LABEL_SEPARATOR = " \u2022 "; function sessionLabel( session: TaskSession, profilesById: Record, position: number, fallbackLabel: string, ): string { const profileLabel = session.agent_profile_id ? profilesById[session.agent_profile_id]?.label : undefined; const snapshotLabel = typeof session.agent_profile_snapshot?.label === "string" ? session.agent_profile_snapshot.label : undefined; const fullLabel = profileLabel ?? snapshotLabel; if (fullLabel) { const parts = fullLabel.split(LABEL_SEPARATOR); return parts[1] ?? parts[0] ?? fullLabel; } if (session.name) return session.name; return fallbackLabel ? fallbackLabel.replace("{{position}}", String(position)) : session.id; } export function buildThreadSessionViews( sessions: readonly TaskSession[], agentProfiles: readonly AgentProfileOption[] = [], fallbackLabel = "", ): ThreadSessionView[] { const profilesById = Object.fromEntries(agentProfiles.map((profile) => [profile.id, profile])); return sortSessions(sessions).map((session, index) => { const profile = session.agent_profile_id ? profilesById[session.agent_profile_id] : undefined; const snapshotAgentName = session.agent_profile_snapshot?.agent_id; return { session, label: sessionLabel(session, profilesById, index + 1, fallbackLabel), agentName: profile?.agent_name ?? (typeof snapshotAgentName === "string" && snapshotAgentName ? snapshotAgentName : null), isPrimary: session.is_primary === true, }; }); } export function ThreadSessionStatusIcon({ status, label, testId, }: { status: ThreadStatus; label: string; testId?: string; }) { const iconProps = { "aria-label": label, "data-testid": testId, className: "h-3.5 w-3.5 shrink-0", }; switch (status.kind) { case "needs-you": return ( ); case "permission": return ( ); case "clarification": return ( ); case "starting": case "working": return ( ); case "failed": return ; case "cancelled": return ; case "finished": return ( ); case "review-ready": return ; case "completed": return ; case "waiting": return ( ); case "created": return ( ); default: return null; } } function DesktopSessionTabs({ views, selectedSessionId, onSelect, }: { views: ThreadSessionView[]; selectedSessionId: string | null; onSelect: (sessionId: string) => void; }) { const tabs: SessionTab[] = views.map((view) => ({ id: view.session.id, label: view.label, icon: , testId: `thread-session-tab-${view.session.id}`, className: "bg-transparent data-[state=active]:bg-muted", })); return (
); } function ThreadSessionTabIndicator({ view }: { view: ThreadSessionView }) { const { t } = useTranslation(); const status = resolveThreadSessionStatus(view.session); if (status.kind === "permission" || status.kind === "clarification") { return ( ); } return ( ); } function ThreadSessionAgentIndicator({ view, testId, }: { view: ThreadSessionView; testId: string; }) { if (isSessionActive(view.session.state)) { return ; } if (view.agentName) { return ( ); } return (