From cf4636706e9f905b1ea4abf3d533d0a48578ca6a Mon Sep 17 00:00:00 2001 From: shaoxianbilly <40623436+shaoxianbilly@users.noreply.github.com> Date: Sat, 5 Sep 2026 02:52:09 +0800 Subject: [PATCH] =?UTF-8?q?fix(canvas):=20=E5=8A=A9=E6=89=8B=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=20-=20=E5=89=A5=E7=A6=BB=E5=B7=A5=E5=85=B7=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E7=BB=93=E6=9E=9C=E4=B8=AD=E7=9A=84=E7=94=BB=E5=B8=83?= =?UTF-8?q?=E5=BF=AB=E7=85=A7=EF=BC=8C=E9=98=B2=E6=AD=A2=E5=B7=A5=E7=A8=8B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=B6=854MB=E5=AF=BC=E8=87=B4=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=AD=BB=E9=94=81=20(#412)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在 canvas-assistant-panel 中 executeOnlineTool 执行 ops 结果返回前,解构剔除冗余的 snapshot、before、after 全量快照对象,仅保留验证摘要及变更元数据; 2. 在 user-data-sync 的 saveRemoteUserDataBatch 同步流程中,增加 sanitizeCanvasProjectForRemoteSync,防御性清洗消息详情中可能残留的历史快照; 3. 避免大画布和多轮对话导致单个画布 JSON 膨胀突破 4MB 限制并引发未确认事务回滚死锁。 --- .../canvas/canvas-assistant-panel.tsx | 3 +- web/src/services/user-data-sync.ts | 41 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/web/src/components/canvas/canvas-assistant-panel.tsx b/web/src/components/canvas/canvas-assistant-panel.tsx index 8809fb7b1..4aa378190 100644 --- a/web/src/components/canvas/canvas-assistant-panel.tsx +++ b/web/src/components/canvas/canvas-assistant-panel.tsx @@ -829,7 +829,8 @@ export function CanvasAssistantPanel({ } const ops = onlineToolToOps(name, args, current, effectiveConfig); const result = await executeOps(ops, { source: "online", conversationId: sessionId, messageId: messageId || sessionId }); - return { ok: result.ok, message: result.changed ? canvasAgentPostconditionMessage(result) : result.noopReason, data: result }; + const { snapshot: _snapshot, before: _before, after: _after, ...cleanData } = result; + return { ok: result.ok, message: result.changed ? canvasAgentPostconditionMessage(result) : result.noopReason, data: cleanData }; } catch (error) { if (isAgentSessionPollingAbort(error)) throw error; return { ok: false, message: error instanceof Error ? error.message : "工具执行失败" }; diff --git a/web/src/services/user-data-sync.ts b/web/src/services/user-data-sync.ts index a801b3231..948646fc0 100644 --- a/web/src/services/user-data-sync.ts +++ b/web/src/services/user-data-sync.ts @@ -369,7 +369,7 @@ async function saveRemoteUserDataBatch(uploaded: Map) { message: "正在保存画布结构", }); } - await upsertRemoteCanvasProject(remotePayload); + await upsertRemoteCanvasProject(sanitizeCanvasProjectForRemoteSync(remotePayload)); acknowledgedProjects.set(source.id, source); if (total > 0) useSyncProgressStore.getState().setProjectProgress(source.id, null); } catch (error) { @@ -513,3 +513,42 @@ function numberValue(value: unknown) { const number = Number(value); return Number.isFinite(number) && number > 0 ? number : undefined; } + +function sanitizeCanvasProjectForRemoteSync(project: T): T { + if (!project || typeof project !== "object") return project; + const clone = { ...(project as Record) }; + if (Array.isArray(clone.chatSessions)) { + clone.chatSessions = clone.chatSessions.map((session) => { + if (!session || typeof session !== "object") return session; + const s = { ...(session as Record) }; + if (Array.isArray(s.messages)) { + s.messages = s.messages.map((message) => { + if (!message || typeof message !== "object" || !message.detail) return message; + const m = { ...(message as Record) }; + if (m.detail && typeof m.detail === "object") { + const d = { ...(m.detail as Record) }; + if (Array.isArray(d.results)) { + d.results = d.results.map((r) => { + if (!r || typeof r !== "object") return r; + const res = { ...(r as Record) }; + if (res.result && typeof res.result === "object") { + const inner = { ...(res.result as Record) }; + if (inner.data && typeof inner.data === "object") { + const { snapshot: _s, before: _b, after: _a, ...restData } = inner.data as Record; + inner.data = restData; + } + res.result = inner; + } + return res; + }); + } + m.detail = d; + } + return m; + }); + } + return s; + }); + } + return clone as T; +}