diff --git a/src/components/agent/chat/components/AgentThreadTimeline.tsx b/src/components/agent/chat/components/AgentThreadTimeline.tsx
index 9d947f9d3..9d8fdf0f9 100644
--- a/src/components/agent/chat/components/AgentThreadTimeline.tsx
+++ b/src/components/agent/chat/components/AgentThreadTimeline.tsx
@@ -58,6 +58,7 @@ import {
resolveTimelineArtifactNavigation,
type ArtifactTimelineOpenTarget,
} from "../utils/artifactTimelineNavigation";
+import { TimelineInlineItem } from "./TimelineInlineItem";
interface AgentThreadTimelineProps {
turn: AgentThreadTurn;
diff --git a/src/components/agent/chat/components/TimelineFlowDemo.tsx b/src/components/agent/chat/components/TimelineFlowDemo.tsx
new file mode 100644
index 000000000..116917dbb
--- /dev/null
+++ b/src/components/agent/chat/components/TimelineFlowDemo.tsx
@@ -0,0 +1,79 @@
+import React from "react";
+import { TimelineInlineItem } from "./TimelineInlineItem";
+import type { AgentThreadItem } from "@/lib/api/agentProtocol";
+
+interface TimelineFlowDemoProps {
+ items: AgentThreadItem[];
+}
+
+/**
+ * 时间线流式展示演示组件
+ *
+ * 这是一个简化的演示,展示新的时间线 + 内联操作设计
+ */
+export function TimelineFlowDemo({ items }: TimelineFlowDemoProps) {
+ // 过滤出需要在时间线上显示的项目
+ const timelineItems = items.filter(
+ (item) =>
+ item.type === "tool_call" ||
+ item.type === "command_execution" ||
+ item.type === "web_search"
+ );
+
+ // 分组:将 Agent 消息和工具调用交错显示
+ const groupedItems: Array<{ type: "text" | "timeline"; content: any }> = [];
+
+ items.forEach((item, index) => {
+ if (item.type === "agent_message") {
+ groupedItems.push({
+ type: "text",
+ content: item,
+ });
+ } else if (
+ item.type === "tool_call" ||
+ item.type === "command_execution" ||
+ item.type === "web_search"
+ ) {
+ // 检查是否已经有一个 timeline 组
+ const lastGroup = groupedItems[groupedItems.length - 1];
+ if (lastGroup && lastGroup.type === "timeline") {
+ lastGroup.content.push(item);
+ } else {
+ groupedItems.push({
+ type: "timeline",
+ content: [item],
+ });
+ }
+ }
+ });
+
+ return (
+
+ {groupedItems.map((group, groupIndex) => {
+ if (group.type === "text") {
+ // Agent 文本消息
+ const item = group.content;
+ return (
+
+ {item.text}
+
+ );
+ } else {
+ // 时间线组
+ const timelineItems = group.content as AgentThreadItem[];
+ return (
+
+ {timelineItems.map((item, itemIndex) => (
+
+ ))}
+
+ );
+ }
+ })}
+
+ );
+}
diff --git a/src/components/agent/chat/components/TimelineInlineItem.tsx b/src/components/agent/chat/components/TimelineInlineItem.tsx
new file mode 100644
index 000000000..cebf0e508
--- /dev/null
+++ b/src/components/agent/chat/components/TimelineInlineItem.tsx
@@ -0,0 +1,287 @@
+import React, { useState } from "react";
+import {
+ ChevronDown,
+ ChevronUp,
+ FileText,
+ Globe,
+ Loader2,
+ Search,
+ TerminalSquare,
+ Edit3,
+ FileEdit,
+ AlertTriangle,
+ CheckCircle2,
+ XCircle,
+} from "lucide-react";
+import { cn } from "@/lib/utils";
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
+import type {
+ AgentThreadItem,
+ AgentThreadToolCallItem,
+ AgentThreadCommandExecutionItem,
+ AgentThreadWebSearchItem,
+} from "@/lib/api/agentProtocol";
+
+interface TimelineInlineItemProps {
+ item: AgentThreadItem;
+ isLast?: boolean;
+}
+
+/**
+ * 类型守卫:检查是否为工具调用类型
+ */
+function isToolCallItem(item: AgentThreadItem): item is AgentThreadToolCallItem {
+ return item.type === "tool_call";
+}
+
+function isCommandExecutionItem(item: AgentThreadItem): item is AgentThreadCommandExecutionItem {
+ return item.type === "command_execution";
+}
+
+function isWebSearchItem(item: AgentThreadItem): item is AgentThreadWebSearchItem {
+ return item.type === "web_search";
+}
+
+/**
+ * 获取工具调用的图标
+ */
+function getToolIcon(toolName: string) {
+ const normalized = toolName.toLowerCase();
+
+ if (normalized.includes("read") || normalized.includes("file")) {
+ return FileText;
+ }
+ if (normalized.includes("bash") || normalized.includes("command")) {
+ return TerminalSquare;
+ }
+ if (normalized.includes("web") || normalized.includes("fetch")) {
+ return Globe;
+ }
+ if (normalized.includes("search") || normalized.includes("grep")) {
+ return Search;
+ }
+ if (normalized.includes("edit")) {
+ return Edit3;
+ }
+ if (normalized.includes("write")) {
+ return FileEdit;
+ }
+
+ return TerminalSquare;
+}
+
+/**
+ * 获取状态图标
+ */
+function getStatusIcon(status: string) {
+ if (status === "running" || status === "pending") {
+ return Loader2;
+ }
+ if (status === "completed" || status === "success") {
+ return CheckCircle2;
+ }
+ if (status === "failed" || status === "error") {
+ return XCircle;
+ }
+ return CheckCircle2;
+}
+
+/**
+ * 格式化工具调用的标题
+ */
+function formatToolCallTitle(item: AgentThreadItem): string {
+ // 命令执行
+ if (isCommandExecutionItem(item)) {
+ const cmd = item.command.trim();
+ const shortCmd = cmd.length > 50 ? cmd.slice(0, 50) + "..." : cmd;
+ return `执行命令 ${shortCmd}`;
+ }
+
+ // Web 搜索
+ if (isWebSearchItem(item)) {
+ return item.query ? `搜索 ${item.query}` : "Web 搜索";
+ }
+
+ // 工具调用
+ if (isToolCallItem(item)) {
+ const toolName = item.tool_name || "操作";
+ const args = item.arguments;
+
+ // 尝试提取有意义的参数
+ if (args && typeof args === "object" && args !== null) {
+ // Read 工具:显示文件路径
+ if ("file_path" in args && typeof args.file_path === "string") {
+ const fileName = args.file_path.split("/").pop() || args.file_path;
+ return `查看 ${fileName}`;
+ }
+
+ // Bash 工具:显示命令
+ if ("command" in args && typeof args.command === "string") {
+ const cmd = args.command.trim();
+ const shortCmd = cmd.length > 50 ? cmd.slice(0, 50) + "..." : cmd;
+ return `执行命令 ${shortCmd}`;
+ }
+
+ // WebFetch 工具:显示 URL
+ if ("url" in args && typeof args.url === "string") {
+ return `访问 ${args.url}`;
+ }
+
+ // Grep 工具:显示搜索模式
+ if ("pattern" in args && typeof args.pattern === "string") {
+ return `搜索 ${args.pattern}`;
+ }
+
+ // Edit 工具:显示文件路径
+ if ("file_path" in args && typeof args.file_path === "string") {
+ const fileName = args.file_path.split("/").pop() || args.file_path;
+ return `编辑 ${fileName}`;
+ }
+
+ // Write 工具:显示文件路径
+ if ("file_path" in args && typeof args.file_path === "string") {
+ const fileName = args.file_path.split("/").pop() || args.file_path;
+ return `写入 ${fileName}`;
+ }
+ }
+
+ return toolName;
+ }
+
+ return "操作";
+}
+
+/**
+ * 截断长文本
+ */
+function truncateText(text: string, maxLines: number = 3): { preview: string; hasMore: boolean } {
+ const lines = text.split("\n");
+
+ if (lines.length <= maxLines) {
+ return { preview: text, hasMore: false };
+ }
+
+ const preview = lines.slice(0, maxLines).join("\n");
+ return { preview, hasMore: true };
+}
+
+export function TimelineInlineItem({ item, isLast }: TimelineInlineItemProps) {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ // 获取工具名称
+ let toolName = "";
+ if (isToolCallItem(item)) {
+ toolName = item.tool_name;
+ } else if (isCommandExecutionItem(item)) {
+ toolName = "Bash";
+ } else if (isWebSearchItem(item)) {
+ toolName = "WebSearch";
+ }
+
+ const ToolIcon = getToolIcon(toolName);
+ const StatusIcon = getStatusIcon(item.status);
+ const title = formatToolCallTitle(item);
+
+ const isRunning = item.status === "in_progress";
+ const isFailed = item.status === "failed";
+
+ // 获取输出内容
+ let output = "";
+ if (isToolCallItem(item)) {
+ output = item.output || item.error || "";
+ } else if (isCommandExecutionItem(item)) {
+ output = item.aggregated_output || item.error || "";
+ } else if (isWebSearchItem(item)) {
+ output = item.output || "";
+ }
+
+ const { preview, hasMore } = truncateText(output, 3);
+
+ // 默认展开失败的工具调用
+ const shouldDefaultExpand = isFailed;
+
+ return (
+
+ {/* 左侧时间线 */}
+
+ {/* 状态图标 */}
+
+
+
+
+ {/* 连接线 */}
+ {!isLast && (
+
+ )}
+
+
+ {/* 右侧内容 */}
+
+
+ {/* 标题行 */}
+
+
+ {title}
+
+ {isRunning && (
+ 正在执行...
+ )}
+
+
+ {/* 输出内容 */}
+ {output && (
+
+ {/* 预览 */}
+
+
+ {shouldDefaultExpand || isExpanded ? output : preview}
+
+
+
+ {/* 展开/收起按钮 */}
+ {hasMore && !shouldDefaultExpand && (
+
+
+
+ )}
+
+ )}
+
+
+
+ );
+}