import type { LogEntry } from "@umt/contracts"; import { Bot, ExternalLink, History } from "lucide-react"; import type { ReactNode } from "react"; import { Link } from "react-router"; import { useEngineRun } from "~/api/engine.ts"; import { type AgentContext, openAgentDrawer } from "~/components/agent/agent-store.ts"; import { DetailSection, DetailSheet } from "~/components/console/detail-sheet.tsx"; import { EmptyState, ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; import { JsonView } from "~/components/console/json-view.tsx"; import { KvList } from "~/components/console/kv-list.tsx"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { TechInfo } from "~/components/console/tech-info.tsx"; import { runTitle } from "~/components/tasks/labels.ts"; import { RunActions, TaskDetailBody, TaskStatusLine } from "~/components/tasks/task-detail.tsx"; import { Button } from "~/components/ui/button.tsx"; import { actorTypeLabel, formatDuration } from "~/lib/labels.ts"; import { formatDateTime } from "~/lib/utils.ts"; import { actorLabel, agentGoalFor, LOG_TYPE_LABEL, logBadge, logsPath, logTitle, objectLogsPath, objectText, TARGET_LABEL, } from "./labels.ts"; // 日志详情抽屉:任务复用原任务详情(业务摘要、时间线、引擎步骤、取消 / 重跑);定时任务运行、后台调查、审计各自一组字段。 // 底部「问智能体」带上这条日志打开智能体抽屉。 /** 选中的日志 → 智能体上下文(单条时带上能识别的对象 id,服务端据此取数) */ export function agentContextFor(entries: LogEntry[]): AgentContext { const ctx: AgentContext = { label: entries.length === 1 ? `日志:${logTitle(entries[0]!)}` : `日志:${entries.length} 条`, goal: agentGoalFor(entries), }; if (entries.length === 1) { const e = entries[0]!; if (e.source === "engine_run") ctx.taskId = e.refId; const o = e.object; if (o?.id) { if (o.type === "download_task") ctx.downloadTaskId = o.id; else if (o.type === "organization_record") ctx.recordId = o.id; else if (o.type === "media_entity") ctx.entityId = o.id; else if (o.type === "site") ctx.siteId = o.id; } } return ctx; } export function AskAgentButton({ entries, size = "sm" }: { entries: LogEntry[]; size?: "sm" | "xs" }) { return ( ); } function LinkRow({ links }: { links: { to: string; label: string; icon?: ReactNode }[] }) { return ( ); } function ScheduleRunBody({ e }: { e: LogEntry }) { const x = e.extra as { jobId?: string; jobKind?: string; trigger?: string; agentRunId?: string | null; summary?: string | null; error?: string | null; }; const jobId = x.jobId ?? e.object?.id ?? ""; return ( <> {x.error ?

{x.error}

: null}
, }, ] : []), ...(x.agentRunId ? [{ to: `/console/agent/${encodeURIComponent(x.agentRunId)}`, label: "巡检会话" }] : []), ]} /> ); } function AgentRunBody({ e }: { e: LogEntry }) { const x = e.extra as { toolCalls?: number; spawnReason?: string | null; model?: string | null }; return ( <> {e.title}, wide: true }, { label: "开始", value: formatDateTime(e.at) }, { label: "耗时", value: e.durationMs == null ? "—" : formatDuration(e.durationMs / 1000) }, { label: "结果", value: e.message, wide: true }, { label: "工具调用", value: x.toolCalls ?? 0 }, ]} /> ); } function AuditBody({ e }: { e: LogEntry }) { const x = e.extra as { action?: string; traceId?: string | null; payload?: unknown }; const o = e.object; return ( <> {o?.id ? ( , }, ]} /> ) : null} ); } /** 任务:按引擎运行 id 取详情(列表里没有这条也能打开,旧链接 ?run= 用) */ function RunSheet({ runId, entry, onOpenRun, onClose, }: { runId: string; entry: LogEntry | null; onOpenRun: (id: string) => void; onClose: () => void; }) { const q = useEngineRun(runId); const r = q.data; return ( !o && onClose()} width="lg" title={r ? runTitle(r) : entry ? logTitle(entry) : "任务"} description={r ? : undefined} footer={ r ? ( <> {entry ? : null} ) : null } > {q.isPending ? ( ) : q.isError ? ( q.refetch()} /> ) : r ? ( ) : null} ); } /** * 日志详情抽屉。open = 日志 id(:<原始 id>);任务类只要 id 就能打开,其余要在已加载的列表里找到这条。 */ export function LogDetailSheet({ logId, entry, onOpenLog, onClose, }: { logId: string | null; entry: LogEntry | null; onOpenLog: (id: string) => void; onClose: () => void; }) { if (!logId) return null; const sep = logId.indexOf(":"); const source = logId.slice(0, sep); const refId = logId.slice(sep + 1); if (source === "engine_run") return onOpenLog(`engine_run:${id}`)} onClose={onClose} />; const badge = entry ? logBadge(entry) : null; return ( !o && onClose()} title={entry ? logTitle(entry) : "日志"} description={ entry ? ( {badge ? {badge.label} : null} {LOG_TYPE_LABEL[entry.type]} · {formatDateTime(entry.at)} {entry.object ? · {objectText(entry.object)} : null} ) : undefined } footer={entry ? : null} > {!entry ? ( ) : entry.source === "schedule_run" ? ( ) : entry.source === "agent_run" ? ( ) : ( )} ); }