diff options
| author | Adam <[email protected]> | 2026-04-08 14:02:23 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-04-08 14:02:23 -0500 |
| commit | 689b1a4b3ab3c33aecc76b84c579b2efce444d6c (patch) | |
| tree | e15a9ceaf044e75b15df7c0918aa9e145c808541 /packages/app/src/utils | |
| parent | d98be39344b8a39d16b62ce927be71a2c6a61a53 (diff) | |
| download | opencode-689b1a4b3ab3c33aecc76b84c579b2efce444d6c.tar.gz opencode-689b1a4b3ab3c33aecc76b84c579b2efce444d6c.zip | |
fix(app): diff list normalization
Diffstat (limited to 'packages/app/src/utils')
| -rw-r--r-- | packages/app/src/utils/diffs.test.ts | 74 | ||||
| -rw-r--r-- | packages/app/src/utils/diffs.ts | 49 |
2 files changed, 123 insertions, 0 deletions
diff --git a/packages/app/src/utils/diffs.test.ts b/packages/app/src/utils/diffs.test.ts new file mode 100644 index 000000000..5fbca469b --- /dev/null +++ b/packages/app/src/utils/diffs.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, test } from "bun:test" +import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2" +import type { Message } from "@opencode-ai/sdk/v2/client" +import { diffs, message } from "./diffs" + +const item = { + file: "src/app.ts", + patch: "@@ -1 +1 @@\n-old\n+new\n", + additions: 1, + deletions: 1, + status: "modified", +} satisfies SnapshotFileDiff + +describe("diffs", () => { + test("keeps valid arrays", () => { + expect(diffs([item])).toEqual([item]) + }) + + test("wraps a single diff object", () => { + expect(diffs(item)).toEqual([item]) + }) + + test("reads keyed diff objects", () => { + expect(diffs({ a: item })).toEqual([item]) + }) + + test("drops invalid entries", () => { + expect( + diffs([ + item, + { file: "src/bad.ts", additions: 1, deletions: 1 }, + { patch: item.patch, additions: 1, deletions: 1 }, + ]), + ).toEqual([item]) + }) +}) + +describe("message", () => { + test("normalizes user summaries with object diffs", () => { + const input = { + id: "msg_1", + sessionID: "ses_1", + role: "user", + time: { created: 1 }, + agent: "build", + model: { providerID: "openai", modelID: "gpt-5" }, + summary: { + title: "Edit", + diffs: { a: item }, + }, + } as unknown as Message + + expect(message(input)).toMatchObject({ + summary: { + title: "Edit", + diffs: [item], + }, + }) + }) + + test("drops invalid user summaries", () => { + const input = { + id: "msg_1", + sessionID: "ses_1", + role: "user", + time: { created: 1 }, + agent: "build", + model: { providerID: "openai", modelID: "gpt-5" }, + summary: true, + } as unknown as Message + + expect(message(input)).toMatchObject({ summary: undefined }) + }) +}) diff --git a/packages/app/src/utils/diffs.ts b/packages/app/src/utils/diffs.ts new file mode 100644 index 000000000..0cb2504fb --- /dev/null +++ b/packages/app/src/utils/diffs.ts @@ -0,0 +1,49 @@ +import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { Message } from "@opencode-ai/sdk/v2/client" + +type Diff = SnapshotFileDiff | VcsFileDiff + +function diff(value: unknown): value is Diff { + if (!value || typeof value !== "object" || Array.isArray(value)) return false + if (!("file" in value) || typeof value.file !== "string") return false + if (!("patch" in value) || typeof value.patch !== "string") return false + if (!("additions" in value) || typeof value.additions !== "number") return false + if (!("deletions" in value) || typeof value.deletions !== "number") return false + if (!("status" in value) || value.status === undefined) return true + return value.status === "added" || value.status === "deleted" || value.status === "modified" +} + +function object(value: unknown): value is Record<string, unknown> { + return !!value && typeof value === "object" && !Array.isArray(value) +} + +export function diffs(value: unknown): Diff[] { + if (Array.isArray(value) && value.every(diff)) return value + if (Array.isArray(value)) return value.filter(diff) + if (diff(value)) return [value] + if (!object(value)) return [] + return Object.values(value).filter(diff) +} + +export function message(value: Message): Message { + if (value.role !== "user") return value + + const raw = value.summary as unknown + if (raw === undefined) return value + if (!object(raw)) return { ...value, summary: undefined } + + const title = typeof raw.title === "string" ? raw.title : undefined + const body = typeof raw.body === "string" ? raw.body : undefined + const next = diffs(raw.diffs) + + if (title === raw.title && body === raw.body && next === raw.diffs) return value + + return { + ...value, + summary: { + ...(title === undefined ? {} : { title }), + ...(body === undefined ? {} : { body }), + diffs: next, + }, + } +} |
