From 69d33df5b6e6dbbdf77e74099ea1089c8e12ba1a Mon Sep 17 00:00:00 2001 From: 7418 <7418@openclaw.ai> Date: Mon, 2 Mar 2026 12:58:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Bridge=20=E5=9B=BE=E7=89=87=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=9C=A8=E6=A1=8C=E9=9D=A2=E7=AB=AF=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E4=B8=AD=E5=9B=9E=E6=98=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit conversation-engine 保存用户消息时,复用桌面端 chat route 的存储格式: - 将 base64 图片写入 .codepilot-uploads/ 目录 - 用 格式嵌入消息 content - 桌面 UI 的 MessageItem.parseMessageFiles() 可直接解析并通过 FileAttachmentDisplay + /api/uploads 渲染图片 将 getSession() 调用提前到 addMessage 之前,确保 workingDirectory 可用。 写入失败时降级为 [N image(s) attached] 文本指示。 Co-Authored-By: Claude Opus 4.6 --- src/lib/bridge/conversation-engine.ts | 39 ++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/lib/bridge/conversation-engine.ts b/src/lib/bridge/conversation-engine.ts index 796abb3..1cfd07c 100644 --- a/src/lib/bridge/conversation-engine.ts +++ b/src/lib/bridge/conversation-engine.ts @@ -6,6 +6,8 @@ * and returns the response text for delivery. */ +import fs from 'fs'; +import path from 'path'; import type { ChannelBinding } from './types'; import type { SSEEvent, TokenUsage, MessageContentBlock, FileAttachment } from '@/types'; import { streamClaude } from '../claude-client'; @@ -87,14 +89,39 @@ export async function processMessage( }, 60_000); try { - // Save user message (with image indicator if attachments present) - const userMsgText = files && files.length > 0 - ? `[${files.length} image(s) attached] ${text}` - : text; - addMessage(sessionId, 'user', userMsgText); + // Resolve session early — needed for workingDirectory and provider resolution + const session = getSession(sessionId); + + // Save user message — persist file attachments to disk using the same + // format as the desktop chat route, so the UI can render them. + let savedContent = text; + if (files && files.length > 0) { + const workDir = binding.workingDirectory || session?.working_directory || ''; + if (workDir) { + try { + const uploadDir = path.join(workDir, '.codepilot-uploads'); + if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir, { recursive: true }); + } + const fileMeta = files.map((f) => { + const safeName = path.basename(f.name).replace(/[^a-zA-Z0-9._-]/g, '_'); + const filePath = path.join(uploadDir, `${Date.now()}-${safeName}`); + const buffer = Buffer.from(f.data, 'base64'); + fs.writeFileSync(filePath, buffer); + return { id: f.id, name: f.name, type: f.type, size: buffer.length, filePath }; + }); + savedContent = `${text}`; + } catch (err) { + console.warn('[conversation-engine] Failed to persist file attachments:', err instanceof Error ? err.message : err); + savedContent = `[${files.length} image(s) attached] ${text}`; + } + } else { + savedContent = `[${files.length} image(s) attached] ${text}`; + } + } + addMessage(sessionId, 'user', savedContent); // Resolve provider - const session = getSession(sessionId); let resolvedProvider: import('@/types').ApiProvider | undefined; const providerId = session?.provider_id || ''; if (providerId && providerId !== 'env') {