Skip to content

agent-host: report git-driven session file diffs#312632

Merged
roblourens merged 8 commits intomainfrom
roblou/agents/git-driven-diff-reporting
Apr 27, 2026
Merged

agent-host: report git-driven session file diffs#312632
roblourens merged 8 commits intomainfrom
roblou/agents/git-driven-diff-reporting

Conversation

@roblourens
Copy link
Copy Markdown
Member

Adds an alternative diff source for agent sessions that derives changes from working-tree git state instead of from edit-tool emissions, so edits made via terminal/shell commands also show up in the Changes view.

Approach

  • IAgentHostGitService gains computeSessionFileDiffs() and showBlob(). Diffs come from git diff --raw --numstat against the merge-base of HEAD and an optional base branch (META_DIFF_BASE_BRANCH in session metadata). Untracked files are included via a temp-index trick (read-tree HEAD + add -A in a throwaway GIT_INDEX_FILE).
  • New gitDiffContent.ts encodes git-blob: content URIs that pin a blob to a session + sha + repo-relative path.
  • AgentService routes git-blob: resourceRead requests to gitService.showBlob(). AgentHostFileSystemProvider.stat() short-circuits git-blob: URIs alongside session-db: so the diff editor's stat-then-read flow works end-to-end.
  • AgentSideEffects._tryComputeGitDiffs runs after each turn (debounced with the existing diff scheduler) and overrides edit-tool diffs when git is available; falls back to the edit-tool path otherwise.

Tests

  • Unit: URI round-trip (gitDiffContent.test.ts), parseGitDiffRawNumstat add/modify/delete/rename + numstat pairing.
  • Integration (real git repo): agentHostGitService.integrationTest.tscomputeSessionFileDiffs + showBlob.
  • Side-effects: agentSideEffects.test.ts — verifies git-driven path overrides edit-tool diffs and falls back when git returns undefined.
  • Protocol: protocol/sessionDiffs.integrationTest.tssession/diffsChanged round-trip with mock git.
  • Gated end-to-end with real Copilot CLI agent: protocol/sessionDiffsRealSdk.integrationTest.ts (skipped without env var).

(Written by Copilot)

Adds an alternative diff source for agent sessions that derives changes
from the working-tree git state instead of from edit-tool emissions, so
edits made via terminal/shell commands also show up in the Changes view.

Approach
--------
* IAgentHostGitService gains computeSessionFileDiffs() and showBlob().
* gitDiffContent.ts encodes 'git-blob:' content URIs that pin a blob to
  a session + sha + repo-relative path.
* AgentService routes 'git-blob:' resourceRead requests to
  gitService.showBlob(); AgentHostFileSystemProvider.stat() short-
  circuits 'git-blob:' alongside 'session-db:' so the diff editor's
  stat-then-read flow works end-to-end.
* AgentSideEffects._tryComputeGitDiffs runs after each turn (debounced
  with the existing diff scheduler) and overrides edit-tool diffs when
  git is available.

Tests
-----
Unit + integration coverage in src/vs/platform/agentHost/test/node/.
Copilot AI review requested due to automatic review settings April 26, 2026 20:28
Adds five tests against AgentHostFileSystemProvider covering the
synthetic content scheme fast-path in stat(): git-blob: and
session-db: URIs must return a File stat directly without trying
to list a parent directory that doesn't exist.

Verified by reverting the git-blob: branch of the  theallowlist
new 'git-blob: stat' and 'full stat-then-read round-trip' tests
fail with EntryNotFound, which is exactly the error the diff
editor surfaced when opening a diff of a new file.

(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a git-driven session diff pipeline in the agent host so the “Changes” view reflects working-tree modifications (including terminal/shell edits) rather than relying solely on edit-tool emissions.

Changes:

  • Extend IAgentHostGitService with computeSessionFileDiffs() (git diff vs merge-base) and showBlob() (git show for baseline content).
  • Introduce git-blob: content URIs and route resourceRead + filesystem stat() so diff editors can read baseline blobs end-to-end.
  • Prefer git-derived diffs in AgentSideEffects with fallback to the existing edit-tracker aggregator; add unit/integration/protocol tests for coverage.
Show a summary per file
File Description
src/vs/platform/agentHost/node/agentHostGitService.ts Implements git-driven diff computation, temp-index flow for untracked files, and blob retrieval.
src/vs/platform/agentHost/node/gitDiffContent.ts Defines git-blob: URI encoding/decoding for baseline blob content.
src/vs/platform/agentHost/node/agentSideEffects.ts Prefers git-driven diff computation per session, with fallback to edit-tracker diffs.
src/vs/platform/agentHost/node/agentService.ts Routes git-blob: resourceRead requests to gitService.showBlob().
src/vs/platform/agentHost/common/agentHostFileSystemProvider.ts Treats git-blob: URIs as readonly files in stat() to satisfy diff editor flows.
src/vs/platform/agentHost/node/copilot/copilotAgent.ts Persists base-branch metadata (META_DIFF_BASE_BRANCH) for merge-base anchoring.
src/vs/platform/agentHost/node/agentHostServerMain.ts Reuses a single git service instance for AgentService and agent registration.
src/vs/platform/agentHost/test/node/gitDiffContent.test.ts Unit tests for git-blob: URI round-tripping and non-matching inputs.
src/vs/platform/agentHost/test/node/agentHostGitService.test.ts Unit tests for untracked parsing, raw/numstat parsing, and empty-tree SHA export.
src/vs/platform/agentHost/test/node/agentHostGitService.integrationTest.ts Real-git integration tests for computeSessionFileDiffs() and showBlob().
src/vs/platform/agentHost/test/node/agentSideEffects.test.ts Verifies git-driven diffs override edit-tracker diffs and fallback behavior.
src/vs/platform/agentHost/test/node/protocol/sessionDiffs.integrationTest.ts Protocol integration test verifying session/diffsChanged broadcast with git diffs.
src/vs/platform/agentHost/test/node/protocol/sessionDiffsRealSdk.integrationTest.ts Gated real-SDK end-to-end test ensuring terminal-driven edits appear in summary.diffs.
src/vs/platform/agentHost/test/node/mockAgent.ts Adds a mock prompt that edits files “via terminal” without file-edit content events.
src/vs/platform/agentHost/test/node/agentService.test.ts Updates git service stubs for the expanded interface.
src/vs/platform/agentHost/test/node/copilotAgent.test.ts Updates git service test stub to match the expanded interface.
src/vs/platform/agentHost/test/node/copilotGitProject.test.ts Updates git service test stub to match the expanded interface.
src/vs/platform/agentHost/test/common/sessionTestHelpers.ts Updates noop git service stub to match the expanded interface.

Copilot's findings

  • Files reviewed: 19/19 changed files
  • Comments generated: 5

Comment thread src/vs/platform/agentHost/node/agentHostGitService.ts Outdated
Comment thread src/vs/platform/agentHost/node/agentHostGitService.ts
Comment thread src/vs/platform/agentHost/test/node/mockAgent.ts Outdated
Comment thread src/vs/platform/agentHost/test/node/agentSideEffects.test.ts Outdated
- agentHostGitService: bump default execFile maxBuffer to 32MB so
  diff output in large repos doesn't fail with ENOBUFS and silently
  drop terminal-driven diffs.
- agentHostGitService.showBlob: validate sha is a hex object name
  before passing it to git, so a malformed git-blob: URI can't
  inject options or resolve to surprising refs.
- mockAgent terminal-edit branch: void+catch the async IIFE so a
  filesystem failure surfaces as a chat delta instead of an
  unhandled rejection (test flake source).
- agentSideEffects.test: replace setTimeout(100) with awaiting the
  SessionDiffsChanged envelope event for both new diff-computation
   deterministic and immune to slow CI.tests
- sessionDiffsRealSdk integrationTest: shell-quote the target file
  path so temp dirs containing spaces don't break the prompt.

(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roblourens and others added 2 commits April 26, 2026 15:21
…stGitService

Replace direct fs/promises and os.tmpdir usage with platform services so
the temp-index dance in computeSessionFileDiffs goes through the same
file system abstraction as the rest of the workbench. (Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirrors the extension's buildTempIndexEnv which sets this flag to avoid
holding the GVFS command hook lock during temp-index git operations.
(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of threading gitService through IAgentSideEffectsOptions, register
it in the local ServiceCollection and inject it via @IAgentHostGitService
decorator, which is the normal pattern everywhere else. (Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 20/20 changed files
  • Comments generated: 1

Comment thread src/vs/platform/agentHost/node/agentHostGitService.ts
roblourens and others added 2 commits April 26, 2026 19:11
…difiedUri for deletions

For deleted files, the 'modified' side of the diff editor must be absent.
The renderer detects deletions via `change.modifiedUri === undefined`, so
producing `IChatSessionFileChange2` (which carries a `uri` key alongside
optional `modifiedUri`) is the right shape.

Previously diffsToChanges returned IChatSessionFileChange (required
modifiedUri) and fell back to the deleted file's pre-deletion path as
modifiedUri, causing the diff editor to try to read a nonexistent file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@roblourens roblourens marked this pull request as ready for review April 27, 2026 02:27
@roblourens roblourens enabled auto-merge (squash) April 27, 2026 02:27
@roblourens roblourens merged commit 8ae0d8e into main Apr 27, 2026
40 of 41 checks passed
@roblourens roblourens deleted the roblou/agents/git-driven-diff-reporting branch April 27, 2026 03:13
@vs-code-engineering vs-code-engineering Bot added this to the 1.118.0 milestone Apr 27, 2026
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.

3 participants