// Adapted from agentui (https://github.com/ashish200729/agentui), components/agents/tool-result.tsx // Copyright (c) 2026 Saurabh Chauhan. MIT License, see ./LICENSE-agentui.md // 改造:状态色 blue/emerald/rose → 中性/success/destructive(并配文字);ActionSwapRollText 滚字去掉; // 折叠用 Disclosure;新增 `badges` 槽(scope/风险/幂等等审计徽标)与 `summary`(收起时的一行结果); // 默认不在完成后自动展开历史卡片(回放时 defaultOpen=false),运行中的卡片展开、完成后按 collapseOnComplete 收起。 import { Ban, Braces, ChevronDown, CircleCheck, CircleX, LoaderCircle, SquareTerminal, Wrench } from "lucide-react"; import { type ReactNode, useCallback, useEffect, useId, useRef, useState } from "react"; import { cn } from "~/lib/utils.ts"; import { CodeBlock, prettyMaybeJson } from "./code-block.tsx"; import { CHEVRON_CLASS, Disclosure } from "./disclosure.tsx"; export type ToolResultStatus = "running" | "success" | "error" | "cancelled"; export type ToolResultKind = "terminal" | "request" | "custom"; const STATUS: Record = { running: { label: "执行中", cls: "text-muted-foreground" }, success: { label: "成功", cls: "text-success" }, error: { label: "失败", cls: "text-destructive" }, cancelled: { label: "已取消", cls: "text-muted-foreground" }, }; function StatusIcon({ status }: { status: ToolResultStatus }) { if (status === "running") return ; if (status === "success") return ; if (status === "error") return ; return ; } function KindIcon({ kind }: { kind: ToolResultKind }) { if (kind === "terminal") return ; if (kind === "request") return ; return ; } export interface ToolResultProps { /** 能力/工具 ID(等宽显示) */ tool: ReactNode; title: ReactNode; children?: ReactNode; status?: ToolResultStatus; kind?: ToolResultKind; /** 耗时等 */ meta?: ReactNode; /** 标题行下方的徽标 */ badges?: ReactNode; /** 收起时显示的一行摘要 */ summary?: ReactNode; icon?: ReactNode; defaultOpen?: boolean; collapseOnComplete?: boolean; className?: string; } export function ToolResult({ tool, title, children, status = "running", kind = "custom", meta, badges, summary, icon, defaultOpen = status === "running", collapseOnComplete = true, className, }: ToolResultProps) { const id = useId(); const [open, setOpenState] = useState(defaultOpen); const prev = useRef(status); const setOpen = useCallback((v: boolean) => setOpenState(v), []); useEffect(() => { if (prev.current !== "running" && status === "running") setOpen(true); if (prev.current === "running" && status !== "running" && collapseOnComplete) setOpen(false); prev.current = status; }, [collapseOnComplete, setOpen, status]); const st = STATUS[status]; return (
{badges ?
{badges}
: null} {!open && summary ? (

{summary}

) : null}
{children}
); } /** 工具输入/输出块:JSON 自动缩进,长内容块内滚动 */ export function ToolResultOutput({ label, children, tone, }: { label: ReactNode; children: string; tone?: "destructive"; }) { const { text, language } = prettyMaybeJson(children); return (

{label}

); }