diff --git a/src/renderer/src/components/native-chat/native-chat-diff.test.ts b/src/renderer/src/components/native-chat/native-chat-diff.test.ts index 7f8fa7af575..9ff176905fb 100644 --- a/src/renderer/src/components/native-chat/native-chat-diff.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-diff.test.ts @@ -63,6 +63,27 @@ describe('diffFromToolCall', () => { ]) }) + it.each([31_999, 32_000, 32_001])( + 'preserves multi-file truncation at %i characters before the next section', + (length) => { + const header = '--- a\n+++ a\n' + const diff = '@@ -1 +1 @@\n-old\n+new\n' + const first = diff + 'x'.repeat(length - header.length - diff.length) + const changes = [ + { path: 'a', diff: first }, + null, + { path: 'ignored' }, + { path: 'b', kind: { move_path: 'c' }, diff: '@@ -1 +1 @@\n-b\n+c' } + ] + const text = `${header}${first}\n--- b\n+++ c\n@@ -1 +1 @@\n-b\n+c` + for (const maxLines of [2, 120, 40_000]) { + expect(diffFromToolCall('apply_patch', { changes }, maxLines)).toEqual( + diffFromText(text, maxLines) + ) + } + } + ) + it('returns null when there is no old/new payload', () => { expect(diffFromToolCall('Edit', { file_path: '/x' })).toBeNull() }) diff --git a/src/shared/native-chat-diff.ts b/src/shared/native-chat-diff.ts index 783cf3d38fa..8096d0824a0 100644 --- a/src/shared/native-chat-diff.ts +++ b/src/shared/native-chat-diff.ts @@ -100,13 +100,15 @@ function patchTextFromToolInput(value: Record): string | null { if (!Array.isArray(value.changes)) { return null } - const sections = value.changes.flatMap((entry) => { + const sections: string[] = [] + let length = 0 + for (const entry of value.changes) { if (typeof entry !== 'object' || entry === null) { - return [] + continue } const change = entry as Record if (typeof change.diff !== 'string') { - return [] + continue } const path = typeof change.path === 'string' ? change.path : 'file' const kind = @@ -114,8 +116,14 @@ function patchTextFromToolInput(value: Record): string | null { ? (change.kind as Record) : null const nextPath = kind && typeof kind.move_path === 'string' ? kind.move_path : path - return [`--- ${path}\n+++ ${nextPath}\n${change.diff}`] - }) + const section = `--- ${path}\n+++ ${nextPath}\n${change.diff}` + length += section.length + (sections.length > 0 ? 1 : 0) + sections.push(section) + // Keep the extra character that tells toLines the diff was truncated. + if (length > MAX_DIFF_CHARS) { + break + } + } return sections.length > 0 ? sections.join('\n') : null }