'use client'; import React from 'react'; import { Activity, CheckCircle2, ChevronDown, Circle, FileText, LoaderCircle, PanelRightOpen, Sparkles, TerminalSquare, Users, X } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { formatTaskRuntimeDuration, formatTaskRuntimeTime } from '@/components/task-runtime/TaskRuntimeShared'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; import { buildTaskUiModel, taskUiStatusClass, taskUiStatusLabel, type TaskUiModel, type TaskUiStatus, } from '@/lib/task-ui-model'; import { containedLongTextClass, containedPreservedLongTextClass } from '@/lib/text-wrapping'; import type { BackendTask, SessionProcessProjection, TaskTimelineCard } from '@/types'; function StatusBadge({ status }: { status: TaskUiStatus }) { const { locale } = useAppI18n(); return ( {taskUiStatusLabel(status, locale)} ); } function ProgressCard({ icon, title, label, status, children, }: { icon: React.ReactNode; title: string; label: string; status: TaskUiStatus; children: React.ReactNode; }) { const { locale } = useAppI18n(); const [open, setOpen] = React.useState(true); return (
{open ? (
{children}
) : null}
); } function SummarySection({ model }: { model: TaskUiModel }) { return (

{model.summary.summary}

); } function SkillSection({ model }: { model: TaskUiModel }) { const { locale } = useAppI18n(); if (model.skills.length === 0) { return

{pickAppText(locale, '暂无 Skill 选择', 'No skill selected yet')}

; } const primary = model.skills[0]; return (
{primary.name}
{primary.createdAt ?
{formatTaskRuntimeTime(primary.createdAt, locale)}
: null} {primary.summary ? (

{primary.summary}

) : null}
); } function ToolsSection({ model }: { model: TaskUiModel }) { const { locale } = useAppI18n(); const attempts = model.attempts.filter((attempt) => attempt.tools.length > 0); if (attempts.length === 0) { return

{pickAppText(locale, '暂无工具调用', 'No tool calls yet')}

; } return (
{attempts.map((attempt) => (
{attempt.title} {attempt.tools.length} calls
{attempt.tools.map((tool) => (
{tool.toolName} {tool.status === 'done' ? : tool.status === 'running' ? : } {taskUiStatusLabel(tool.status, locale)} {formatSidebarToolDuration(tool, locale)}
))}
))}
); } function formatSidebarToolDuration(tool: TaskUiModel['tools'][number], locale: string): string { if (typeof tool.durationMs === 'number') { return formatTaskRuntimeDuration(tool.durationMs, locale); } if (tool.status === 'running' && tool.createdAt) { const startMs = new Date(tool.createdAt).getTime(); if (!Number.isNaN(startMs)) { return formatTaskRuntimeDuration(Date.now() - startMs, locale); } } return '-'; } function flattenAgents(model: TaskUiModel) { const output: Array<{ id: string; name: string; status: TaskUiStatus; depth: number }> = []; const visit = (node: TaskUiModel['agentTree'][number], depth: number) => { output.push({ id: node.runId, name: node.title || node.name, status: node.status, depth }); node.children.forEach((child) => visit(child, depth + 1)); }; model.agentTree.forEach((node) => visit(node, 0)); return output; } function AgentSection({ model }: { model: TaskUiModel }) { const { locale } = useAppI18n(); const rows = flattenAgents(model).slice(0, 6); if (!model.team.hasTeam) { return (
{pickAppText(locale, '本轮为 Main Agent 单线程执行,未启动 Agent Team。', 'This run uses the Main Agent only; no Agent Team was started.')}
); } if (rows.length === 0) { return

{pickAppText(locale, 'Agent Team 已启动,等待节点数据', 'Agent Team started; waiting for node data')}

; } return (
{rows[0]?.name}
{rows.map((node) => (
{node.name} {node.status === 'done' ? : node.status === 'running' ? : } {taskUiStatusLabel(node.status, locale)}
))}
); } function statusForSummary(task: BackendTask): TaskUiStatus { if (task.status === 'awaiting_acceptance' || task.status === 'closed') return 'done'; if (task.status === 'running') return 'running'; return 'waiting'; } function primarySkillName(model: TaskUiModel) { return model.skills[0]?.name || ''; } function hasExecutionStructure(model: TaskUiModel): boolean { return model.team.hasTeam || model.agentTree.length > 0; } function toolStatus(model: TaskUiModel): TaskUiStatus { if (model.tools.some((tool) => tool.status === 'running')) return 'running'; if (model.tools.some((tool) => tool.status === 'error')) return 'error'; return model.tools.length ? 'done' : 'waiting'; } function agentStatus(model: TaskUiModel): TaskUiStatus { if (model.agentTree.some((node) => node.status === 'running' || node.children.some((child) => child.status === 'running'))) return 'running'; if (!model.team.hasTeam) return 'waiting'; if (model.agentTree.some((node) => node.status === 'error' || node.children.some((child) => child.status === 'error'))) return 'error'; return model.agentTree.length ? 'done' : model.team.status; } function ProgressPanel({ task, process, cards, isLive, onClose, }: { task: BackendTask | null; process: SessionProcessProjection | null; cards: TaskTimelineCard[]; isLive: boolean; onClose?: () => void; }) { const { locale } = useAppI18n(); const model = task ? buildTaskUiModel({ task, process: process ?? { runs: [], events: [], artifacts: [] }, cards, locale, }) : null; return (

{pickAppText(locale, '当前会话的运行进度', 'Current Session Progress')}

{isLive ? : null} {isLive ? pickAppText(locale, '任务时间线实时更新', 'Task timeline updates live') : pickAppText(locale, '与任务详情时间线一致', 'Matches the Task detail timeline')}

{onClose ? ( ) : null}
{model ? (
} title={pickAppText(locale, '任务摘要', 'Task summary')} label={model.summary.title} status={task ? statusForSummary(task) : 'waiting'} > {model.skills.length > 0 ? ( } title={pickAppText(locale, 'Skill 选择', 'Skill selection')} label={primarySkillName(model)} status={model.skills[0]?.status || 'waiting'} > ) : null} {model.tools.length > 0 ? ( } title={pickAppText(locale, '工具调用', 'Tool calls')} label={pickAppText(locale, `${model.tools.length} 个工具调用`, `${model.tools.length} tool calls`)} status={toolStatus(model)} > ) : null} {hasExecutionStructure(model) ? ( } title={pickAppText(locale, '执行结构', 'Execution structure')} label={ model.team.hasTeam ? pickAppText(locale, `Agent Team · ${model.team.outcome}`, `Agent Team · ${model.team.outcome}`) : pickAppText(locale, 'Agent run', 'Agent run') } status={agentStatus(model)} > ) : null}
) : (
{pickAppText(locale, '当前会话暂无运行任务', 'No running task in this session')}
)}
); } export function CurrentSessionProgressSidebar({ task, process, cards, isLive, }: { task: BackendTask | null; process: SessionProcessProjection | null; cards: TaskTimelineCard[]; isLive: boolean; }) { const { locale } = useAppI18n(); const [mobileOpen, setMobileOpen] = React.useState(false); return ( <> {mobileOpen ? (
) : null} ); }