'use client'; import { AlertTriangle, ArrowRightCircle, Bot, CheckCircle2, ClipboardList, ChevronDown, FileText, GitBranch, History, ListChecks, Sparkles, TerminalSquare, ThumbsUp, Users, Wrench, } from 'lucide-react'; import { TaskRuntimeStatusBadge, formatTaskRuntimeTime } from '@/components/task-runtime/TaskRuntimeShared'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent } from '@/components/ui/card'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; import type { TaskRuntimeStatus } from '@/lib/task-runtime'; import { containedJsonTextClass, containedLongTextClass, containedPreservedLongTextClass } from '@/lib/text-wrapping'; import type { TaskTimelineCard as TaskTimelineCardView, TaskTimelineCardType } from '@/types'; import { TaskAcceptanceControls, type TaskFeedbackItem, type TaskFeedbackType } from './TaskAcceptanceCard'; type Props = { card: TaskTimelineCardView; resultAcceptance?: TaskResultAcceptance; reviewTargetId?: string; }; export type TaskResultAcceptance = { sessionId: string; runId: string | null; taskStatus: string; feedbackItems: TaskFeedbackItem[]; actionBusy: string | null; revision?: string; onRevisionChange?: (value: string) => void; onSubmit: (feedbackType: TaskFeedbackType, comment?: string) => Promise; }; const RUNTIME_STATUSES = new Set(['queued', 'running', 'waiting', 'blocked', 'done', 'error', 'cancelled']); function isRuntimeStatus(status: string): status is TaskRuntimeStatus { return RUNTIME_STATUSES.has(status); } function iconForType(type: TaskTimelineCardType) { switch (type) { case 'task_created': return ClipboardList; case 'plan': return ListChecks; case 'skill': return Sparkles; case 'tool_call': return Wrench; case 'tool_result': return TerminalSquare; case 'next_step': return ArrowRightCircle; case 'agent_team': return Users; case 'agent_progress': return Bot; case 'agent_handoff': return GitBranch; case 'artifact': return FileText; case 'error': return AlertTriangle; case 'result': return CheckCircle2; case 'result_history': return History; case 'acceptance': return ThumbsUp; } } function detailsJson(details: Record): string { try { return JSON.stringify(details, null, 2); } catch { return String(details); } } function cardTypeLabel(type: TaskTimelineCardType, locale: string) { const labels: Record = { task_created: ['任务', 'Task'], plan: ['计划', 'Plan'], skill: ['Skill', 'Skill'], tool_call: ['工具调用', 'Tool call'], tool_result: ['工具结果', 'Tool result'], next_step: ['下一步', 'Next step'], agent_team: ['Agent Team', 'Agent team'], agent_progress: ['Agent', 'Agent'], agent_handoff: ['交接', 'Handoff'], artifact: ['产物', 'Artifact'], error: ['异常', 'Error'], result: ['结果', 'Result'], result_history: ['历史结果', 'Result history'], acceptance: ['验收', 'Acceptance'], }; const label = labels[type]; return pickAppText(locale, label[0], label[1]); } function humanStatus(status: string, locale: string) { const labels: Record = { open: ['已创建', 'Open'], running: ['执行中', 'Running'], awaiting_acceptance: ['等待验收', 'Awaiting acceptance'], needs_revision: ['需要修改', 'Needs revision'], closed: ['已完成', 'Closed'], abandoned: ['已放弃', 'Abandoned'], accept: ['接受', 'Accepted'], satisfied: ['接受', 'Accepted'], revise: ['请求修改', 'Revision requested'], abandon: ['放弃任务', 'Abandoned'], warning: ['提醒', 'Warning'], }; const label = labels[status]; return label ? pickAppText(locale, label[0], label[1]) : status; } function historyVersions(details: Record | undefined): Array> { const versions = details?.versions; return Array.isArray(versions) ? versions.filter((item): item is Record => Boolean(item) && typeof item === 'object') : []; } function renderHistoryStatus(version: Record, locale: string) { const status = String(version.acceptanceType || version.status || ''); return status ? humanStatus(status, locale) : pickAppText(locale, '历史版本', 'Previous version'); } function TaskResultHistory({ card }: { card: TaskTimelineCardView }) { const { locale } = useAppI18n(); const versions = historyVersions(card.details); return (
{pickAppText(locale, '展开历史版本', 'Show previous versions')}
{versions.map((version, index) => (
{pickAppText(locale, `第 ${index + 1} 轮结果`, `Version ${index + 1}`)}
{renderHistoryStatus(version, locale)}
{version.result ?

{String(version.result)}

: null} {version.comment ? (
{pickAppText(locale, '修改意见', 'Revision note')}: {String(version.comment)}
) : null}
))}
); } export function TaskTimelineCard({ card, resultAcceptance, reviewTargetId }: Props) { const { locale } = useAppI18n(); const Icon = iconForType(card.type); const shouldRenderResultAcceptance = Boolean(card.type === 'result' && resultAcceptance && card.runId === resultAcceptance.runId); return (

{card.title}

{cardTypeLabel(card.type, locale)}
{card.actorName ? {card.actorName} : null} {formatTaskRuntimeTime(card.createdAt, locale)} {card.runId ? {card.runId.slice(0, 8)} : null}
{card.status ? ( isRuntimeStatus(card.status) ? ( ) : ( {humanStatus(card.status, locale)} ) ) : null}
{card.summary ?

{card.summary}

: null} {shouldRenderResultAcceptance ? (
) : null} {card.type === 'result_history' ? : card.details ? (
{pickAppText(locale, '详情 JSON', 'Details JSON')}
                  {detailsJson(card.details)}
                
) : null}
); }