Просмотр исходного кода

fix(app): deduplicate diff summaries linearly (#37414)

Luke Parker 1 месяц назад
Родитель
Сommit
3a1c6df9e2

+ 3 - 13
packages/app/src/pages/session/timeline/rows.ts

@@ -1,7 +1,8 @@
 import { parseCommentNote, readCommentMetadata } from "@/utils/comment-note"
-import { AssistantMessage, Part, SessionStatus, SnapshotFileDiff, UserMessage } from "@opencode-ai/sdk/v2"
+import { AssistantMessage, Part, SessionStatus, UserMessage } from "@opencode-ai/sdk/v2"
 import { groupParts, renderable, type PartGroup } from "@opencode-ai/session-ui/message-part"
 import { TimelineRow, type SummaryDiff } from "./timeline-row"
+import { uniqueSummaryDiffs } from "./summary-diffs"
 
 export { TimelineRow, type SummaryDiff } from "./timeline-row"
 
@@ -137,14 +138,7 @@ export namespace Timeline {
 
     if (isActive && status === "retry") rows.push(new TimelineRow.Retry({ userMessageID: userMessage.id }))
 
-    const diffs = (userMessage.summary?.diffs ?? [])
-      .reduceRight<SummaryDiff[]>((result, diff) => {
-        if (!isSummaryDiff(diff)) return result
-        if (result.some((item) => item.file === diff.file)) return result
-        result.push(diff)
-        return result
-      }, [])
-      .reverse()
+    const diffs = uniqueSummaryDiffs(userMessage.summary?.diffs)
     if (diffs.length > 0 && (status === "idle" || !isActive)) {
       rows.push(
         new TimelineRow.DiffSummary({
@@ -169,10 +163,6 @@ export namespace Timeline {
     return rows
   }
 
-  function isSummaryDiff(value: SnapshotFileDiff): value is SummaryDiff {
-    return typeof value.file === "string"
-  }
-
   function reasoningHeading(text: string) {
     const markdown = text.replace(/\r\n?/g, "\n")
     const html = markdown.match(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/i)

+ 42 - 0
packages/app/src/pages/session/timeline/summary-diffs.test.ts

@@ -0,0 +1,42 @@
+import { describe, expect, test } from "bun:test"
+import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2"
+import { uniqueSummaryDiffs } from "./summary-diffs"
+
+const diff = (file: string, additions: number) =>
+  ({
+    file,
+    additions,
+    deletions: 0,
+  }) satisfies SnapshotFileDiff
+
+describe("uniqueSummaryDiffs", () => {
+  test("drops entries without files and preserves unique input", () => {
+    const alpha = diff("alpha.ts", 1)
+    const beta = diff("beta.ts", 1)
+    const invalid = { additions: 1, deletions: 0 } satisfies SnapshotFileDiff
+
+    expect(uniqueSummaryDiffs(undefined)).toEqual([])
+    expect(uniqueSummaryDiffs([])).toEqual([])
+    expect(uniqueSummaryDiffs([invalid])).toEqual([])
+
+    const result = uniqueSummaryDiffs([alpha, invalid, beta])
+    expect(result).toEqual([alpha, beta])
+    expect(result[0]).toBe(alpha)
+    expect(result[1]).toBe(beta)
+  })
+
+  test("keeps the last diff per file in the legacy display order", () => {
+    const oldAlpha = diff("alpha.ts", 1)
+    const oldBeta = diff("beta.ts", 1)
+    const newAlpha = diff("alpha.ts", 2)
+    const charlie = diff("charlie.ts", 1)
+    const newBeta = diff("beta.ts", 2)
+
+    const result = uniqueSummaryDiffs([oldAlpha, oldBeta, newAlpha, charlie, newBeta])
+
+    expect(result).toEqual([newAlpha, charlie, newBeta])
+    expect(result[0]).toBe(newAlpha)
+    expect(result[1]).toBe(charlie)
+    expect(result[2]).toBe(newBeta)
+  })
+})

+ 20 - 0
packages/app/src/pages/session/timeline/summary-diffs.ts

@@ -0,0 +1,20 @@
+import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2"
+import type { SummaryDiff } from "./timeline-row"
+
+export function uniqueSummaryDiffs(diffs: SnapshotFileDiff[] | undefined) {
+  const files = new Set<string>()
+  return (diffs ?? [])
+    .reduceRight<SummaryDiff[]>((result, diff) => {
+      if (!isSummaryDiff(diff)) return result
+      const file = diff.file
+      if (files.has(file)) return result
+      files.add(file)
+      result.push(diff)
+      return result
+    }, [])
+    .reverse()
+}
+
+function isSummaryDiff(diff: SnapshotFileDiff): diff is SummaryDiff {
+  return typeof diff.file === "string"
+}