Skip to content

Replace 'Files Changed' with session-scoped agent changes (#174) - #175

Merged
jeonghun-jj-lee merged 20 commits into
local/amicodefrom
jj/174-session-scoped-files-changed
Aug 10, 2026
Merged

Replace 'Files Changed' with session-scoped agent changes (#174)#175
jeonghun-jj-lee merged 20 commits into
local/amicodefrom
jj/174-session-scoped-files-changed

Conversation

@jeonghun-jj-lee

Copy link
Copy Markdown
Contributor

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.

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.
@jeonghun-jj-lee
jeonghun-jj-lee marked this pull request as ready for review August 10, 2026 21:41
@jeonghun-jj-lee
jeonghun-jj-lee merged commit 2424f56 into local/amicode Aug 10, 2026
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace 'Files Changed' with session-scoped agent changes

1 participant