From f2dcafc2c56aad241f1ead2ac03c818ba0dffcd7 Mon Sep 17 00:00:00 2001 From: Marius Date: Thu, 26 Mar 2026 15:47:04 +0100 Subject: [PATCH] fix(cli): strip bloated file contents from tool metadata to fix session loading perf (#7706) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Edit, apply_patch, and user message summary were storing full before/after file contents in metadata — causing sessions to balloon to 90+ MB and crash the VS Code extension on load. Strip before/after from edit filediff, apply_patch files[], and summary.diffs both at write time (new data) and read time (existing data). --- .../kilo-ui/src/components/message-part.tsx | 24 ++++--- packages/opencode/src/session/message-v2.ts | 72 +++++++++++++++++-- packages/ui/src/components/message-part.tsx | 24 ++++--- 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx index ac6b5a4cf2..0ff6de64f2 100644 --- a/packages/kilo-ui/src/components/message-part.tsx +++ b/packages/kilo-ui/src/components/message-part.tsx @@ -2007,8 +2007,8 @@ interface ApplyPatchFile { relativePath: string type: "add" | "update" | "delete" | "move" diff: string - before: string - after: string + before?: string + after?: string additions: number deletions: number movePath?: string @@ -2158,7 +2158,7 @@ ToolRegistry.register({ - +
} > -
- -
+ +
+ +
+
)} diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index e468d1f260..1e84d3433e 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -503,6 +503,62 @@ export namespace MessageV2 { }) export type WithParts = z.infer + // kilocode_change start - strip bloated metadata fields from stored parts to prevent multi-MB payloads + // This handles both legacy data that was stored with full file contents and keeps the API response lean. + function stripPartMetadata(part: Part): Part { + if (part.type !== "tool") return part + const { state } = part + if (state.status !== "completed" && state.status !== "running") return part + const meta = state.metadata + if (!meta) return part + + let changed = false + let next = meta + + // Strip edit tool's filediff.before/after (full file contents) + if (meta.filediff && (meta.filediff.before || meta.filediff.after)) { + const { before, after, ...rest } = meta.filediff + next = { ...next, filediff: rest } + changed = true + } + + // Strip apply_patch tool's files[].before/after (full file contents per file) + if (Array.isArray(meta.files) && meta.files.length > 0 && meta.files[0]?.before !== undefined) { + next = { + ...next, + files: meta.files.map((f: Record) => { + const { before, after, ...rest } = f + return rest + }), + } + changed = true + } + + if (!changed) return part + return { ...part, state: { ...state, metadata: next } } as Part + } + + function stripMessageMetadata(info: Info): Info { + // Strip summary.diffs before/after from user messages (can be 20+ MB) + if (info.role !== "user") return info + const user = info as User + if (!user.summary?.diffs?.length) return info + const has = user.summary.diffs.some((d: Snapshot.FileDiff) => d.before || d.after) + if (!has) return info + return { + ...user, + summary: { + ...user.summary, + diffs: user.summary.diffs.map(({ before, after, ...rest }: Snapshot.FileDiff) => ({ + ...rest, + before: "", + after: "", + })), + }, + } as Info + } + // kilocode_change end + export function toModelMessages( input: WithParts[], model: Provider.Model, @@ -766,12 +822,12 @@ export namespace MessageV2 { .all(), ) for (const row of partRows) { - const part = { + const part = stripPartMetadata({ ...row.data, id: row.id, sessionID: row.session_id, messageID: row.message_id, - } as MessageV2.Part + } as MessageV2.Part) // kilocode_change - strip bloated metadata on read const list = partsByMessage.get(row.message_id) if (list) list.push(part) else partsByMessage.set(row.message_id, [part]) @@ -779,7 +835,7 @@ export namespace MessageV2 { } for (const row of rows) { - const info = { ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info + const info = stripMessageMetadata({ ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info) // kilocode_change yield { info, parts: partsByMessage.get(row.id) ?? [], @@ -796,7 +852,13 @@ export namespace MessageV2 { db.select().from(PartTable).where(eq(PartTable.message_id, message_id)).orderBy(PartTable.id).all(), ) return rows.map( - (row) => ({ ...row.data, id: row.id, sessionID: row.session_id, messageID: row.message_id }) as MessageV2.Part, + (row) => + stripPartMetadata({ + ...row.data, + id: row.id, + sessionID: row.session_id, + messageID: row.message_id, + } as MessageV2.Part), // kilocode_change - strip bloated metadata on read ) }) @@ -808,7 +870,7 @@ export namespace MessageV2 { async (input): Promise => { const row = Database.use((db) => db.select().from(MessageTable).where(eq(MessageTable.id, input.messageID)).get()) if (!row) throw new Error(`Message not found: ${input.messageID}`) - const info = { ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info + const info = stripMessageMetadata({ ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info) // kilocode_change return { info, parts: await parts(input.messageID), diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index b00e06d288..66cdd29cc8 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1840,8 +1840,8 @@ interface ApplyPatchFile { relativePath: string type: "add" | "update" | "delete" | "move" diff: string - before: string - after: string + before?: string + after?: string additions: number deletions: number movePath?: string @@ -1956,7 +1956,7 @@ ToolRegistry.register({ - +
} > -
- -
+ +
+ +
+