Replace 'Files Changed' with session-scoped agent changes (#174) - #175
Merged
jeonghun-jj-lee merged 20 commits intoAug 10, 2026
Conversation
Replace the stub Session.diff with actual logic that computes a net diff (session-start snapshot vs current state) filtered to files the agent touched via PatchParts. - Add Snapshot.Service as a dependency of Session's layer - Query all messages for step-start hash and PatchPart file sets - Call snapshot.diffFull(from, current), filter to agent-touched files - Filter out zero-change entries (reverted edits) - Update HTTP handler to call Session.diff instead of per-message summary.diff - Update and add tests for the new session-scoped behavior
Remove the git/branch/turn mode system from the Files Changed panel. Replace it with a single query to GET /session/:id/diff which returns the net diff of all files the agent touched during the session. - Remove ChangeMode/VcsMode types and all mode-related signals - Remove vcsQuery, vcsKey, refreshVcs, changesOptions, turnDiffs, nogit - Add sessionDiffQuery that calls sdk().client.session.diff() - Simplify loadReviewDiff to look up from the already-fetched diffs - Replace mode selector dropdown with static 'Session changes' label - Simplify empty state (no more mode-specific messages) - Keep file tree and diff viewer components untouched
For completed sessions, the working tree has moved on — snapshot.track() returns the current state, not what the agent left behind. Use the last step-finish snapshot hash as the 'to' reference instead. Only fall back to snapshot.track() when no step-finish exists (session still running).
…lable When a session has no step-start/step-finish snapshot hashes (old sessions, snapshots disabled, or GC'd), fall back to the per-message summary.diffs that were stored at summarization time. Also gracefully handle diffFull failures (GC'd git objects) by catching and falling through to the fallback path. This ensures older sessions still show their file changes even when the snapshot-based computation can't produce results.
The session messages already carry summary.diffs (the same data rendered inline as 'Edit [file] +N -M'). Just aggregate those across all user messages — no server roundtrip, no binary rebuild, works immediately. Remove: sessionDiffQuery, sessionDiffKey, refreshSessionDiff, wantsReview, createQuery/skipToken/useQueryClient/debounce imports.
The summary.diffs field on user messages is empty in practice (the summarizer's computeDiff produces nothing without snapshot hashes). The inline 'Edit [file]' display renders from tool parts — that's the real data source. Read from completed edit/write/patch/apply_patch tool parts in the sync store, matching the same source the chat uses.
The tool part's state.metadata.filediff carries the full diff data: file path, patch text, additions, and deletions. Use it directly so the review panel can render actual diffs (not 'Binary file'). Fall back to state.title for the relative display path.
- Default review diff style to 'unified' (single column, better for side panel) - Fix 'Files Changed' tab label in session-side-panel.tsx (two locations) Known limitation: only files edited via the 'edit' tool (with filediff metadata) appear in the panel. Bash-written files lack this metadata and won't show until the snapshot/patch system is active.
sync().data.message[id] only has messages loaded in the current viewport (paginated). Use messages() from the timeline model which has all loaded messages, so the Files Changed panel shows all edited files in the session.
…server query The session has no patch parts and empty summary.diffs, but 39 completed edit/write tool parts carry filediff metadata with full patch content. Add a third fallback in Session.diff that extracts from these tool parts. Frontend: restore the server query as primary source (it queries ALL messages regardless of pagination), with client-side tool parts as immediate fallback while the server loads.
…panels - Remove 'Session changes' title from toolbar (redundant with tab name) - Add overflow:hidden on toolbar + flex-shrink:0 on toolbar groups so controls clip instead of overlapping when narrow - Set min-width:320px on the review panel container to prevent the resize handle from shrinking below usable toolbar width
…d off Remove justify-content:space-between so toolbar items sit adjacent with a small gap instead of being pushed to opposite edges. The start group (file counter + arrows) shrinks first; the end group (toggle) never clips. Reduced min-width to 200px.
reviewReady always returns true (client-side data is always available immediately). Add placeholderData:[] so the query never transitions through an empty state on session switch. This prevents layout shifts in the review panel that were triggering the auto-scroll anchor to snap back to bottom.
When a session first opens, reviewDiffs() transitions from [] to [files] as data loads. This flipped hasReview from false→true, causing the review panel to switch from empty state to file list — a layout shift that reset the scroll anchor. Making hasReview always true keeps the panel layout stable during loading.
The old code used createMemo (turnDiffs). Our replacement was a plain function, meaning every consumer subscribed directly to messages(), sync().data.part, and the query — re-rendering on EVERY update during initial message load. Wrapping in createMemo batches these: consumers only re-render when the computed file list actually changes.
…d fires On first open of a session (no cached measurements), the virtualizer renders items starting from offset 0 for one frame before the onMount rAF calls scrollToEnd(). This produces a visible flash of the top messages before snapping to the bottom. Fix: set opacity:0 on the ScrollView during cold-bottom-mount and reveal it in the same rAF that scrolls to end — the user never sees the intermediate top-of-page state.
On first open (no cached measurements), parts arrive asynchronously and append new rows to the timeline. With followOnAppend:true and scrollEndThreshold:80, the virtualizer treated the user as 'at the end' even after scrolling up a couple lines (<80px), and forcibly followed each appended row back to the bottom. Fix: make followOnAppend a reactive getter gated by shouldAnchorBottom() (which is false once userScrolled is set). On re-open the measurements are cached so no appends happen regardless.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #174
Replaces the git/branch/turn mode system in the Files Changed panel with a session-scoped view showing all files the agent touched.
Backend: Session.diff computes net diffs from snapshots, falls back to tool part filediff metadata.
Frontend: Removed VCS mode selector; reads from server endpoint + client-side tool parts.
TUI: Removed mode selector.
UI: Unified diff default, toolbar overflow fix, removed dead expand/collapse toggle.