diff --git a/apps/web/app/components/agent/agent-conversation.tsx b/apps/web/app/components/agent/agent-conversation.tsx
index f117740f..c58ea56e 100644
--- a/apps/web/app/components/agent/agent-conversation.tsx
+++ b/apps/web/app/components/agent/agent-conversation.tsx
@@ -36,6 +36,7 @@ import { ApprovalCard, type ApprovalCardStatus } from "~/components/agentui/appr
import { type CitationItem, CitationList } from "~/components/agentui/citations.tsx";
import { CHEVRON_CLASS, Disclosure } from "~/components/agentui/disclosure.tsx";
import { FileDiff } from "~/components/agentui/file-diff.tsx";
+import { Markdown } from "~/components/agentui/markdown.tsx";
import {
Message,
MessageAvatar,
@@ -427,7 +428,8 @@ export function AgentConversation({
announce={false}
showActions={!active && idx === lastText}
>
- {it.text}
+ {/* 流式中按原文显示(半截 Markdown 会闪),结束后按安全子集渲染 */}
+ {streaming ? it.text :
仅供参考,不作为完成依据。
diff --git a/apps/web/app/components/agentui/markdown.test.ts b/apps/web/app/components/agentui/markdown.test.ts new file mode 100644 index 00000000..402ca90a --- /dev/null +++ b/apps/web/app/components/agentui/markdown.test.ts @@ -0,0 +1,74 @@ +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it } from "vitest"; +import { Markdown, parseMarkdown, safeHref } from "./markdown.tsx"; + +const html = (text: string) => renderToStaticMarkup(createElement(Markdown, { text })); + +describe("Markdown 安全子集", () => { + it("块级:标题、列表、表格、代码块、引用、分隔线", () => { + const src = [ + "## 结论", + "", + "**当前订阅 2 个**,来源 `inventory.query`:", + "", + "| # | 标题 | 年份 |", + "|---|:---|---:|", + "| 1 | 沙丘2 | 2024 |", + "| 2 | 沙丘:预言 | 2024 |", + "", + "- 缺 1080p", + "- 缺 4K", + "", + "1. 先搜", + "2. 再提交", + "", + "> 仅供参考", + "", + "---", + "```json", + '{"a":1}', + "```", + ].join("\n"); + const blocks = parseMarkdown(src); + expect(blocks.map((b) => b.type)).toEqual(["heading", "paragraph", "table", "list", "list", "quote", "hr", "code"]); + const out = html(src); + expect(out).toContain("