"use client"; import { useTranslation } from "react-i18next"; import type { TFunction } from "i18next"; import { IconAlertTriangle, IconLoader2, IconRefresh } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { DialogFooter } from "@kandev/ui/dialog"; import { DrawerFooter } from "@kandev/ui/drawer"; import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip"; import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint"; import { canApproveAgentRuntimeUpdate, resolveRuntimeActiveVersion, resolveRuntimeEffectiveVersion, resolveRuntimeOperation, resolveRuntimeVersionPair, runtimeOperationLabelKey, } from "@/lib/agent-runtime-update"; import type { AgentUpdateJob, AgentUpdatePreview, AgentUpdateStatus, InstallJob } from "@/lib/api"; import type { RuntimeUpdate } from "@/lib/types/http"; import { AgentRuntimeUpdateSurface } from "./agent-runtime-update-surface"; import { RuntimeVersionPicker } from "./runtime-version-picker"; import { useAgentUpdateDialogState } from "./use-agent-update-dialog-state"; const UPDATE_AGENT_KEY = "agents:updateAgent"; const ACTIVE_UPDATE_STATUSES = new Set([ "queued", "resolving", "updating", "refreshing", ]); // The job `status` values are the wire enum; only the phase labels are copy, so // they travel as catalog keys and resolve at render. const UPDATE_PHASE_KEYS: Partial> = { queued: "agents:updatePhaseQueued", resolving: "agents:updatePhaseResolving", updating: "agents:updatePhaseUpdating", refreshing: "agents:updatePhaseRefreshing", }; function updatePhase(t: TFunction, status: AgentUpdateJob["status"] | undefined): string | null { const key = status ? UPDATE_PHASE_KEYS[status] : undefined; return key ? t(key) : null; } function UpdateResult({ agentName, job }: { agentName: string; job?: AgentUpdateJob }) { const { t } = useTranslation(); if (!job) return null; const isUpToDate = job.operation === "up_to_date"; if (job.status === "succeeded" && job.refresh_error) { return (

{t("agents:runtimeUpdatedRefreshFailed", { error: job.refresh_error })}

); } if (job.status === "succeeded") { return (

{isUpToDate ? t("agents:runtimeAlreadyUpToDate") : t("agents:runtimeUpdatedSuccess")}

); } if (job.status === "failed") { return (

{job.error || t("agents:runtimeUpdateFailed")}

); } return null; } type UpdateBodyProps = { agentName: string; preview: AgentUpdatePreview | null; loading: boolean; previewError: string | null; approveError: string | null; job?: AgentUpdateJob; onRetryPreview: () => void; selectedTarget: string; onSelectTarget: (targetVersion: string) => void; selectedUseDefault: boolean; onSelectDefault: () => void; starting: boolean; }; function RuntimeVersionSummary({ agentName, preview, job, }: { agentName: string; preview: AgentUpdatePreview; job?: AgentUpdateJob; }) { const { t } = useTranslation(); const { currentVersion, targetVersion } = resolveRuntimeVersionPair(preview, job); const activeVersion = resolveRuntimeActiveVersion(preview, job); const operation = resolveRuntimeOperation(preview, job); const isUpToDate = operation === "up_to_date"; const effectiveVersion = resolveRuntimeEffectiveVersion(preview, job); return (

{t(runtimeOperationLabelKey(operation))}

{isUpToDate ? currentVersion : `${currentVersion} → ${targetVersion}`}

{activeVersion &&

{t("agents:activeRuntimeVersion", { version: activeVersion })}

}

{t("agents:effectiveRuntimeVersion", { version: effectiveVersion })}

{preview.default_version && (

{t("agents:kandevDefaultVersion", { version: preview.default_version })}

)}
); } function RuntimeUpdatePreviewDetails({ agentName, preview, selectedTarget, selectedUseDefault, loading, starting, job, onSelectTarget, onSelectDefault, }: { agentName: string; preview: AgentUpdatePreview; selectedTarget: string; selectedUseDefault: boolean; loading: boolean; starting: boolean; job?: AgentUpdateJob; onSelectTarget: (targetVersion: string) => void; onSelectDefault: () => void; }) { const { t } = useTranslation(); return ( <>

{t("agents:runtimeUpdateExplainer")}

{t("agents:runtimeUpdateSessionsNote")}

{t("agents:commandThatWillRun")}

          {preview.command_string}
        
); } function UpdateBody({ agentName, preview, loading, previewError, approveError, job, onRetryPreview, selectedTarget, onSelectTarget, selectedUseDefault, onSelectDefault, starting, }: UpdateBodyProps) { const { t } = useTranslation(); const phase = updatePhase(t, job?.status); return (
{loading && !preview && (

{t("agents:checkingLatestRuntimeVersion")}

)} {previewError && (

{previewError}

)} {approveError && (

{t("agents:unableToStartUpdate", { error: approveError })}

)} {preview && ( )} {phase && (

{phase}

)} {job?.output && (
          {job.output}
        
)}
); } type UpdateFooterProps = { agentName: string; preview: AgentUpdatePreview | null; previewError: string | null; job?: AgentUpdateJob; loading: boolean; starting: boolean; installInFlight: boolean; onApprove: () => void; onClose: () => void; mobile?: boolean; }; function UpdateFooter({ agentName, preview, previewError, job, loading, starting, installInFlight, onApprove, onClose, mobile, }: UpdateFooterProps) { const { t } = useTranslation(); const updateInFlight = Boolean(job && ACTIVE_UPDATE_STATUSES.has(job.status)); const canRetry = job?.status === "failed"; const canApprove = canApproveAgentRuntimeUpdate({ preview, job, previewError, loading, updateInFlight, starting, installInFlight, }); const showApprove = !job || canRetry; const content = ( <> {showApprove && ( )} ); if (mobile) { return ( {content} ); } return {content}; } function UpdateTrigger({ agentName, displayName, runtimeUpdateStatus, installInFlight, onOpen, }: { agentName: string; displayName: string; runtimeUpdateStatus?: AgentUpdateStatus; installInFlight: boolean; onOpen: () => void; }) { const { t } = useTranslation(); const statusLabel = runtimeUpdateStatusLabel(t, displayName, runtimeUpdateStatus); return ( {runtimeUpdateStatus?.check_state === "update_available" && ( {installInFlight ? t("agents:agentInstallationInProgress") : statusLabel} ); } function runtimeUpdateStatusLabel( t: TFunction, displayName: string, status?: AgentUpdateStatus, ): string { if (status?.check_state === "update_available") { return t("agents:updateAvailableWithVersions", { name: displayName, current: status.effective_version, latest: status.latest_version, }); } if (status?.check_state === "unknown") { return t("agents:runtimeUpdateStatusUnknown", { name: displayName }); } return t(UPDATE_AGENT_KEY, { name: displayName }); } export function AgentRuntimeUpdateControl({ agentName, displayName, runtimeUpdate, runtimeUpdateStatus, job, installJob, onPreview, onUpdate, }: { agentName: string; displayName: string; runtimeUpdate: RuntimeUpdate; runtimeUpdateStatus?: AgentUpdateStatus; job?: AgentUpdateJob; installJob?: InstallJob; onPreview: ( agentName: string, targetVersion?: string, useDefault?: boolean, ) => Promise; onUpdate: ( agentName: string, targetVersion: string, useDefault?: boolean, ) => Promise; }) { const { isMobile } = useResponsiveBreakpoint(); const { activeJob, approve, approveError, handleOpenChange, loading, loadPreview, open, preview, previewError, selectTarget, selectDefault, selectedTarget, selectedUseDefault, starting, } = useAgentUpdateDialogState({ agentName, job, onPreview, onUpdate }); const installInFlight = installJob?.status === "queued" || installJob?.status === "running"; if (!runtimeUpdate.supported) return null; const body = ( void loadPreview(selectedUseDefault ? undefined : selectedTarget, selectedUseDefault) } selectedTarget={selectedTarget} onSelectTarget={selectTarget} selectedUseDefault={selectedUseDefault} onSelectDefault={selectDefault} starting={starting} /> ); const footer = (mobile = false) => ( void approve()} onClose={() => handleOpenChange(false)} mobile={mobile} /> ); return ( <> handleOpenChange(true)} /> ); }