Skip to content

Commit d373c56

Browse files
kitlangtonDeveloper
andauthored
fix(session): accept legacy summary diffs (anomalyco#26579)
Co-authored-by: Developer <temp@example.com>
1 parent 5fa5d87 commit d373c56

12 files changed

Lines changed: 88 additions & 15 deletions

File tree

packages/app/src/pages/session/session-side-panel.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ import { createOpenSessionFileTab, createSessionTabs, getTabReorderIndex, type S
2828
import { setSessionHandoff } from "@/pages/session/handoff"
2929
import { useSessionLayout } from "@/pages/session/session-layout"
3030

31+
type RenderDiff = (SnapshotFileDiff & { file: string }) | VcsFileDiff
32+
33+
function renderDiff(value: SnapshotFileDiff | VcsFileDiff): value is RenderDiff {
34+
return typeof value.file === "string"
35+
}
36+
3137
export function SessionSidePanel(props: {
3238
canReview: () => boolean
3339
diffs: () => (SnapshotFileDiff | VcsFileDiff)[]
@@ -70,7 +76,8 @@ export function SessionSidePanel(props: {
7076
})
7177
const treeWidth = createMemo(() => (fileOpen() ? `${layout.fileTree.width()}px` : "0px"))
7278

73-
const diffFiles = createMemo(() => props.diffs().map((d) => d.file))
79+
const diffs = createMemo(() => props.diffs().filter(renderDiff))
80+
const diffFiles = createMemo(() => diffs().map((d) => d.file))
7481
const kinds = createMemo(() => {
7582
const merge = (a: "add" | "del" | "mix" | undefined, b: "add" | "del" | "mix") => {
7683
if (!a) return b
@@ -81,7 +88,7 @@ export function SessionSidePanel(props: {
8188
const normalize = (p: string) => p.replaceAll("\\\\", "/").replace(/\/+$/, "")
8289

8390
const out = new Map<string, "add" | "del" | "mix">()
84-
for (const diff of props.diffs()) {
91+
for (const diff of diffs()) {
8592
const file = normalize(diff.file)
8693
const kind = diff.status === "added" ? "add" : diff.status === "deleted" ? "del" : "mix"
8794

packages/opencode/src/cli/cmd/export.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ function span(id: string, value: { value: string; start: number; end: number })
2323
}
2424
}
2525

26-
function diff(kind: string, diffs: { file: string; patch?: string }[] | undefined) {
26+
function diff(kind: string, diffs: { file?: string; patch?: string }[] | undefined) {
2727
return diffs?.map((item, i) => ({
2828
...item,
29-
file: redact(`${kind}-file`, String(i), item.file),
29+
file: item.file === undefined ? undefined : redact(`${kind}-file`, String(i), item.file),
3030
patch: item.patch === undefined ? undefined : redact(`${kind}-patch`, String(i), item.patch),
3131
}))
3232
}

packages/opencode/src/cli/cmd/tui/plugin/api.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ function stateApi(sync: ReturnType<typeof useSync>): TuiPluginApi["state"] {
148148
return sync.data.session.length
149149
},
150150
diff(sessionID) {
151-
return sync.data.session_diff[sessionID] ?? []
151+
return (sync.data.session_diff[sessionID] ?? []).flatMap((item) =>
152+
item.file === undefined ? [] : [{ ...item, file: item.file }],
153+
)
152154
},
153155
todo(sessionID) {
154156
return sync.data.todo[sessionID] ?? []

packages/opencode/src/session/summary.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export const layer = Layer.effect(
134134
.read<Snapshot.FileDiff[]>(["session_diff", input.sessionID])
135135
.pipe(Effect.catch(() => Effect.succeed([] as Snapshot.FileDiff[])))
136136
const next = diffs.map((item) => {
137+
if (item.file === undefined) return item
137138
const file = unquoteGitPath(item.file)
138139
if (file === item.file) return item
139140
return { ...item, file }

packages/opencode/src/snapshot/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ export const Patch = Schema.Struct({
2020
export type Patch = typeof Patch.Type
2121

2222
export const FileDiff = Schema.Struct({
23-
file: Schema.String,
2423
// Optional because legacy/imported `summary_diffs` on disk may omit
25-
// the patch text (see #26574). Required-Schema rejected the whole
26-
// /session/<id>/diff response and broke session loading on Desktop.
24+
// file details and patch text. Required Schema rejected the whole
25+
// session response and broke session loading on Desktop.
26+
file: Schema.optional(Schema.String),
2727
patch: Schema.optional(Schema.String),
2828
additions: NonNegativeInt,
2929
deletions: NonNegativeInt,

packages/opencode/test/server/httpapi-session.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,36 @@ describe("session HttpApi", () => {
297297
),
298298
)
299299

300+
it.live(
301+
"serves sessions with migrated summary diffs missing file details",
302+
withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) =>
303+
Effect.gen(function* () {
304+
const session = yield* createSession(tmp.path, { title: "legacy diff" })
305+
yield* Effect.sync(() =>
306+
Database.use((db) =>
307+
db
308+
.update(SessionTable)
309+
.set({
310+
summary_additions: 1,
311+
summary_deletions: 0,
312+
summary_files: 1,
313+
summary_diffs: [{ additions: 1, deletions: 0 }],
314+
})
315+
.where(eq(SessionTable.id, session.id))
316+
.run(),
317+
),
318+
)
319+
320+
const response = yield* request(pathFor(SessionPaths.get, { sessionID: session.id }), {
321+
headers: { "x-opencode-directory": tmp.path },
322+
})
323+
324+
expect(response.status).toBe(200)
325+
expect((yield* json<Session.Info>(response)).summary?.diffs).toEqual([{ additions: 1, deletions: 0 }])
326+
}),
327+
),
328+
)
329+
300330
it.live(
301331
"serves lifecycle mutation routes",
302332
withTmp({ git: true, config: { formatter: false, lsp: false, share: "disabled" } }, (tmp) =>

packages/opencode/test/session/schema-decoding.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,26 @@ describe("Session.Info", () => {
8383
expect(Session.Info.zod.parse(input)).toEqual(input)
8484
})
8585

86+
test("accepts migrated summary diffs without file details", () => {
87+
const input = {
88+
id: sessionID,
89+
slug: "legacy-diff",
90+
projectID,
91+
directory: "/tmp/proj",
92+
title: "Legacy diff",
93+
version: "0.1.0",
94+
summary: {
95+
additions: 1,
96+
deletions: 0,
97+
files: 1,
98+
diffs: [{ additions: 1, deletions: 0 }],
99+
},
100+
time: { created: 1, updated: 2 },
101+
}
102+
expect(decode(input)).toEqual(input)
103+
expect(Session.Info.zod.parse(input)).toEqual(input)
104+
})
105+
86106
test("rejects unbranded session id", () => {
87107
const bad = { id: "not-a-session-id" } as unknown
88108
expect(() => decode(bad)).toThrow()

packages/opencode/test/session/snapshot-tool-race.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ it.live("tool execution produces non-empty session diff (snapshot race)", () =>
238238
expect(tool?.state.status).toBe("completed")
239239

240240
// Poll for diff — summarize() is fire-and-forget
241-
let diff: Array<{ file: string }> = []
241+
let diff: Array<{ file?: string }> = []
242242
for (let i = 0; i < 50; i++) {
243243
diff = yield* summary.diff({ sessionID: session.id })
244244
if (diff.length > 0) break

packages/sdk/js/src/v2/gen/types.gen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export type PermissionRequest = {
119119
}
120120

121121
export type SnapshotFileDiff = {
122-
file: string
122+
file?: string
123123
patch?: string
124124
additions: number
125125
deletions: number

packages/ui/src/components/session-diff.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ type LegacyDiff = {
1212
status?: "added" | "deleted" | "modified"
1313
}
1414

15-
type ReviewDiff = SnapshotFileDiff | VcsFileDiff | LegacyDiff
15+
type SnapshotDiff = SnapshotFileDiff & { file: string }
16+
type ReviewDiff = SnapshotDiff | VcsFileDiff | LegacyDiff
1617

1718
export type ViewDiff = {
1819
file: string

0 commit comments

Comments
 (0)