From cc7a6bf07673e73683729870123d73ee45ee783d Mon Sep 17 00:00:00 2001 From: aic0d3r Date: Mon, 22 Jun 2026 19:45:25 +0200 Subject: [PATCH 1/2] fix(session): restore session summary from per-turn diffs #30127 zeroed the session-level summary (files/additions/deletions) for performance and kept only message-scoped turn diffs. The TUI sidebar "Modified Files" section reads the session-level aggregate, so it has been empty since v1.16.0 (#30877, #32852). Re-aggregate session.summary from the cheap per-message turn diffs that #30127 preserved. Each turn already computes and stores its diff on the message; summing those into the session summary avoids the expensive full-session snapshot recompute that #30127 removed. Each touched file appears once (last turn wins), matching the pre-#30127 sidebar behavior. --- packages/opencode/src/session/summary.ts | 41 ++++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index 370870935ad6..f89ed84a5f27 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -103,16 +103,14 @@ export const layer = Layer.effect( sessionID: SessionID messageID: MessageID }) { - yield* sessions.setSummary({ - sessionID: input.sessionID, - summary: { - additions: 0, - deletions: 0, - files: 0, - }, - }) - yield* events.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: [] }) - if ((yield* config.get()).snapshot === false) return + if ((yield* config.get()).snapshot === false) { + yield* sessions.setSummary({ + sessionID: input.sessionID, + summary: { additions: 0, deletions: 0, files: 0 }, + }) + yield* events.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: [] }) + return + } const all = yield* sessions.messages({ sessionID: input.sessionID }).pipe(Effect.orDie) if (!all.length) return @@ -124,6 +122,29 @@ export const layer = Layer.effect( const msgDiffs = yield* computeDiff({ messages }) target.info.summary = { ...target.info.summary, diffs: msgDiffs } yield* sessions.updateMessage(target.info) + + // Re-aggregate the session-level summary from the cheap per-message + // turn diffs (already computed above and on prior turns). This restores + // the "Modified Files" sidebar, which reads session.summary, without + // the expensive full-session snapshot diff that #30127 removed. + // Each touched file appears once (last turn wins). + const byFile = new Map() + for (const msg of all) { + for (const d of msg.info.summary?.diffs ?? []) { + byFile.set(d.file ?? `__nofile_${byFile.size}`, d) + } + } + const aggregate = [...byFile.values()] + yield* sessions.setSummary({ + sessionID: input.sessionID, + summary: { + additions: aggregate.reduce((sum, x) => sum + x.additions, 0), + deletions: aggregate.reduce((sum, x) => sum + x.deletions, 0), + files: aggregate.length, + diffs: aggregate, + }, + }) + yield* events.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: aggregate }) }) const diff = Effect.fn("SessionSummary.diff")(function* (input: { sessionID: SessionID; messageID?: MessageID }) { From 2fd73f8f4f62166b26e8aca745bd6239194cc497 Mon Sep 17 00:00:00 2001 From: aic0d3r Date: Mon, 22 Jun 2026 20:01:33 +0200 Subject: [PATCH 2/2] fix: type-narrow to user messages for diff aggregation Assistant messages have summary: boolean, not the structured Summary with diffs. Only user messages carry per-turn diffs. --- packages/opencode/src/session/summary.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index f89ed84a5f27..4d03e51c2de7 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -130,7 +130,10 @@ export const layer = Layer.effect( // Each touched file appears once (last turn wins). const byFile = new Map() for (const msg of all) { - for (const d of msg.info.summary?.diffs ?? []) { + if (msg.info.role !== "user") continue + const diffs = msg.info.summary?.diffs + if (!diffs) continue + for (const d of diffs) { byFile.set(d.file ?? `__nofile_${byFile.size}`, d) } }