diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 3dd0b80fc..df38dbc43 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -541,6 +541,65 @@ function writeCanvasOperationShadowChunk(params: { ); } +const AGENT_EXECUTION_PREFACE_INTERNAL_PATTERN = + /(?:^|\n)\s*(?:#{1,6}\s*)?(?:需求理解|图片分析|执行计划|输出设置|优化后提示词|使用建议)(?:\s|[::]|$)|【\s*(?:需求理解|图片分析|执行计划|输出设置|优化后提示词|使用建议)\s*】|提示词|自动优化|模型|提交生成|回写|流程|步骤/; + +function buildAgentExecutionPrefaceText(params: { + friendlyMessage?: string | null; + fallback: string; +}) { + const fallback = String(params.fallback || "").replace(/\s+/g, " ").trim(); + const rawMessage = String(params.friendlyMessage || "") + .replace(//g, "") + .trim(); + + if (!rawMessage || AGENT_EXECUTION_PREFACE_INTERNAL_PATTERN.test(rawMessage)) { + return fallback; + } + + const candidate = rawMessage + .split(/\r?\n/) + .map((line) => line.replace(/^[-*]\s*/, "").trim()) + .find(Boolean); + + if (!candidate || AGENT_EXECUTION_PREFACE_INTERNAL_PATTERN.test(candidate)) { + return fallback; + } + + const firstSentence = candidate.match(/^(.{1,80}?[。!?!?~~])/u)?.[1] || candidate; + const compact = firstSentence.replace(/\s+/g, " ").trim().slice(0, 80); + return /[。!?!?~~]$/.test(compact) ? compact : `${compact}。`; +} + +async function writeAgentExecutionPrefaceMessage(params: { + writer: StreamWriter; + chatId: string; + label: string; + friendlyMessage?: string | null; + fallback: string; +}) { + const text = buildAgentExecutionPrefaceText({ + friendlyMessage: params.friendlyMessage, + fallback: params.fallback, + }); + if (!text) return null; + + const messageId = generateUUID(); + writeAssistantTextChunk(params.writer, text, params.label, messageId); + + try { + await saveAssistantTextOnlyMessage({ + chatId: params.chatId, + messageId, + text, + }); + } catch (error) { + chatApiLogger.warn("[Chat API] Failed to persist agent execution preface:", error); + } + + return messageId; +} + async function replayAssistantMessagesForDuplicateRequest({ chatId, userMessageId, @@ -7715,6 +7774,16 @@ export async function POST(request: Request) { ) { const directImageStream = createUIMessageStream({ execute: async ({ writer }) => { + await writeAgentExecutionPrefaceMessage({ + writer, + chatId: id, + label: "agent:generate_image:preface", + friendlyMessage: smartAutoFriendlyMessage, + fallback: + directAgentToolTargetImageUrls.length > 0 + ? "收到,我会参考你选的图片来生成新画面,稍等一下。" + : "好的,我按你的描述来做一版画面,稍等我处理一下。", + }); writeAgentProgressChunk( writer, buildDirectAgentImageContextProgress({ @@ -7917,6 +7986,16 @@ export async function POST(request: Request) { ) { const directVideoStream = createUIMessageStream({ execute: async ({ writer }) => { + await writeAgentExecutionPrefaceMessage({ + writer, + chatId: id, + label: "agent:generate_video:preface", + friendlyMessage: smartAutoFriendlyMessage, + fallback: + directAgentToolTargetImageUrls.length > 0 + ? "收到,我会参考你选的画面来生成视频,稍等一下。" + : "好的,我按你的需求来做这段视频,稍等我处理一下。", + }); const assistantMessageId = generateUUID(); writeAgentProgressChunk( writer, @@ -8123,6 +8202,13 @@ export async function POST(request: Request) { const directToolLabel = getDirectAgentToolLabel(directAgentToolCapability); const directToolStream = createUIMessageStream({ execute: async ({ writer }) => { + await writeAgentExecutionPrefaceMessage({ + writer, + chatId: id, + label: `agent:${directAgentToolCapability}:preface`, + friendlyMessage: smartAutoFriendlyMessage, + fallback: `好的,我来帮你${directToolLabel},稍等我处理一下。`, + }); writeAgentProgressChunk( writer, buildDirectAgentImageContextProgress({ diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 2e48a2353..345fef046 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -12225,10 +12225,7 @@ export function CanvasChatPanel({ const isAgentTargetClarificationActive = shouldShowAgentTargetLockCard && activeAgentTargetLock?.stage === "clarify"; const shouldShowAgentProgressCard = - activeComposerMode === "agent" && - Boolean(activeAgentProgress) && - activeAgentProgress?.stage !== "completed" && - activeAgentProgress?.stage !== "failed"; + false; const agentTargetLockPreviewItems = useMemo(() => { if (!activeAgentTargetLock) return [] as AgentTargetLockRefPreview[]; const targets = Array.isArray(activeAgentTargetLock.targets) ? activeAgentTargetLock.targets : [];