agent-host: surface session git state via SessionState._meta#312543
Merged
roblourens merged 11 commits intomainfrom Apr 26, 2026
Merged
agent-host: surface session git state via SessionState._meta#312543roblourens merged 11 commits intomainfrom
roblourens merged 11 commits intomainfrom
Conversation
The agent host process now computes per-session git state (branch, GitHub remote, ahead/behind, uncommitted changes) for sessions that have a working directory and publishes it through the protocol's per-session `_meta`. The Agents app reads it from `SessionState._meta` (not `SessionSummary`) and surfaces it via `ISessionWorkspace`, lighting up existing UI in the Changes view (e.g. ahead/behind indicators, branch info). Highlights: - New `AgentHostGitService` (server-side) computes git state by shelling out to git; refreshed on session open and after each turn. - New `SessionMetaChanged` action propagates `_meta` deltas without a full list refresh. - Client-side `AgentHostSessionAdapter` retains `_project` / `_workingDirectory` / `_meta` so the workspace observable can be rebuilt when only `_meta` changes. - `baseBranchProtected` is computed client-side from `git.branchProtection` config, so the workspace shape no longer carries it as a field on `ISessionRepository`. - `update()` only overwrites `_meta` when the source actually provides one (SessionSummary feeds have no `_meta` field), so the polling refresh path no longer clobbers good state pushed via `SessionState`. (Written by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a per-session git-state “side channel” for agent-host sessions by computing git info in the agent-host process and publishing it via SessionState._meta, then adapting the Agents app/session providers to rebuild ISessionWorkspace when _meta changes so existing Changes UI can light up (branch/ahead-behind/uncommitted/GitHub remote).
Changes:
- Introduces
SessionState._meta+SessionMetaChangedaction in the AHP protocol and reducers, plus typed helpers (readSessionGitState,withSessionGitState) for the reservedgitmeta slot. - Adds server-side git probing (
AgentHostGitService.getSessionGitState) and hooks to refresh/publish git state on session open/restore and after each completed turn. - Updates agent-host session adapters/providers to retain project/workingDirectory/meta and rebuild the workspace on
_metaupdates; updates UI logic to read git state from workspace repositories (vs session summary metadata).
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostChatContribution.test.ts | Adds regression test ensuring list metadata only includes working directory (git state no longer on summary). |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionListController.ts | Stops emitting empty metadata; only includes remote host + workingDirectoryPath when present. |
| src/vs/sessions/services/sessions/common/session.ts | Extends ISessionRepository to carry git-related fields sourced from _meta.git. |
| src/vs/sessions/contrib/terminal/test/browser/sessionsTerminalContribution.test.ts | Updates tests for ISessionRepository shape change (removes explicit baseBranchProtected: undefined). |
| src/vs/sessions/contrib/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts | Stubs IConfigurationService required by provider changes. |
| src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts | Passes git state + branch protection patterns into workspace builder; injects configuration service. |
| src/vs/sessions/contrib/copilotChatSessions/browser/copilotChatSessionsProvider.ts | Stops expecting baseBranchProtected in metadata-derived repository objects. |
| src/vs/sessions/contrib/chat/test/browser/sessionsConfigurationService.test.ts | Updates test repository object to match optional baseBranchProtected. |
| src/vs/sessions/contrib/chat/test/browser/sessionWorkspacePicker.test.ts | Updates mock provider repository object for new ISessionRepository optional fields. |
| src/vs/sessions/contrib/changes/browser/changesViewModel.ts | Reads git state from workspace repositories for agent-host sessions; adjusts hasGitRepository derivation. |
| src/vs/sessions/contrib/agentHost/test/browser/localAgentHostSessionsProvider.test.ts | Stubs IConfigurationService required by provider changes. |
| src/vs/sessions/contrib/agentHost/browser/localAgentHostSessionsProvider.ts | Passes git state + branch protection patterns into workspace builder; injects configuration service. |
| src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts | Retains _meta and rebuilds workspace on SessionMetaChanged via session-state subscription. |
| src/vs/sessions/common/agentHostSessionWorkspace.ts | Adds branch protection pattern helpers and maps _meta.git into ISessionRepository fields + workspace key. |
| src/vs/platform/agentHost/test/node/copilotGitProject.test.ts | Updates test git service stub for new getSessionGitState API. |
| src/vs/platform/agentHost/test/node/copilotAgent.test.ts | Updates test git service stub for new getSessionGitState API. |
| src/vs/platform/agentHost/test/node/agentSideEffects.test.ts | Updates tests for new onTurnComplete callback requirement. |
| src/vs/platform/agentHost/test/node/agentService.test.ts | Adds tests validating git state is attached to SessionState._meta.git when workingDirectory exists. |
| src/vs/platform/agentHost/test/node/agentHostGitService.test.ts | Adds unit tests for git parsing helpers (status v2, remotes, default branch ref). |
| src/vs/platform/agentHost/test/node/agentHostGitService.integrationTest.ts | Adds on-disk integration tests that spawn real git to validate computed session git state. |
| src/vs/platform/agentHost/node/agentSideEffects.ts | Calls onTurnComplete after idle to trigger post-turn git refresh. |
| src/vs/platform/agentHost/node/agentService.ts | Wires in git state refresh/publish on create/restore and post-turn; restores persisted _meta. |
| src/vs/platform/agentHost/node/agentHostStateManager.ts | Adds setSessionMeta dispatching SessionMetaChanged to update SessionState._meta. |
| src/vs/platform/agentHost/node/agentHostGitService.ts | Implements getSessionGitState by shelling out to git + adds parsing helpers. |
| src/vs/platform/agentHost/common/state/sessionState.ts | Defines SessionMeta, ISessionGitState, reserved git key, and helper read/write functions. |
| src/vs/platform/agentHost/common/state/protocol/version/registry.ts | Registers new actions as introduced in protocol v1. |
| src/vs/platform/agentHost/common/state/protocol/state.ts | Adds SessionState._meta and SessionSummary.activity; updates some schema JSDoc. |
| src/vs/platform/agentHost/common/state/protocol/reducers.ts | Handles SessionActivityChanged and SessionMetaChanged in reducer. |
| src/vs/platform/agentHost/common/state/protocol/actions.ts | Adds SessionActivityChangedAction and SessionMetaChangedAction. |
| src/vs/platform/agentHost/common/state/protocol/action-origin.generated.ts | Updates generated action unions and client-dispatchable map for new actions. |
| src/vs/platform/agentHost/common/state/protocol/.ahp-version | Syncs vendored protocol version to 84e5779. |
| src/vs/platform/agentHost/common/agentService.ts | Extends IAgentSessionMetadata with _meta?: SessionMeta. |
Copilot's findings
Comments suppressed due to low confidence (2)
src/vs/platform/agentHost/node/agentService.ts:618
- Same as above: this comment says git state attaches under
summary._meta.git, but the implementation usesSessionState._metaviasetSessionMeta. Please update to avoid misleading future readers.
// Lazily compute git state for sessions with a working directory;
// attaches under `summary._meta.git` once ready.
this._attachGitState(session, meta.workingDirectory);
src/vs/platform/agentHost/node/agentService.ts:343
_attachGitStateis fire-and-forget and can be triggered multiple times (session open + after each turn). Because results are applied asynchronously, a slower earlier probe can overwrite a newer git state snapshot and regress the UI. Consider tracking a per-session request counter / cancellation token and only applying the latest completed probe’s result.
private _attachGitState(session: URI, workingDirectory: URI | undefined): void {
if (!this._gitService || !workingDirectory) {
return;
}
this._gitService.getSessionGitState(workingDirectory).then(
gitState => {
if (!gitState) {
return;
}
const sessionKey = session.toString();
const current = this._stateManager.getSessionState(sessionKey)?._meta;
this._stateManager.setSessionMeta(sessionKey, withSessionGitState(current, gitState));
},
e => {
this._logService.warn(`[AgentService] Failed to compute git state for ${session}`, e);
},
);
- Files reviewed: 32/32 changed files
- Comments generated: 4
- Doc updates: reference SessionState._meta (not SessionSummary._meta) in agent service interface and the setMeta delta path. - changesViewModel.activeSessionHasGitRepositoryObs now derives the git-backed signal from surfaced git state on the workspace's first repository (uncommittedChanges/incomingChanges/outgoingChanges/ upstreamBranchName) rather than from mere workspace presence, so we don't enable git-specific UI for non-git working directories. (Written by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Plumb 'branchName' through: - ISessionRepository (new optional field) - buildAgentHostSessionWorkspace + agentHostSessionWorkspaceKey (gitFields) - ChangesViewModel.activeSessionStateObs (fall back to workspace repo) - ChangesViewPane.getTreeRootInfo: only render parens when branchName known Also subscribe to activeSessionStateObs in the changes-tree autorun so the root rebuilds once branchName arrives asynchronously. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…(Written by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously AgentService was constructed without a git service (the optional _gitService parameter was never passed), so _attachGitState always bailed with 'hasGitService=false' and no branch name was ever computed. The fix creates AgentHostGitService before AgentService and passes it as the fifth constructor argument. Also removes debug logging added during investigation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The optional `_gitService?` parameter was the root cause of git metadata never reaching the client: `agentHostMain.ts` and `agentHostServerMain.ts` constructed AgentService without passing it, and `_attachGitState` silently bailed at runtime. Making the dep required forces all callers (now and in the future) to wire it correctly at compile time. Also adds a regression test for the `subscribe()` lazy-fire path, which was the intended client-visible mechanism for surfacing branch name on sessions that already exist in the state manager. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Branch name on non-worktree sessions wasn't a goal. Restores the original guard so `(branch)` only appears when the session has a worktree. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- changesViewModel: fall back to workspaceRepository.baseBranchName so agent-host sessions get branch protection UI when only the workspace repo carries baseBranchName - changesView: add comment explaining the intentional dependency read on activeSessionStateObs in the tree-update autorun - agentService: dedupe _ skip setSessionMeta when theattachGitState newly computed git state equals the current _meta.git, avoiding action churn after every turn Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bpasero
approved these changes
Apr 26, 2026
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.
The agent host process now computes per-session git state (branch, GitHub remote, ahead/behind, uncommitted changes) for sessions that have a working directory and publishes it through the protocol's per-session
_meta. The Agents app reads it fromSessionState._meta(notSessionSummary) and surfaces it viaISessionWorkspace, lighting up existing UI in the Changes view (e.g. ahead/behind indicators, branch info).This is a narrower successor to #311815: just the metadata pipe, without skill syncing or toolbar button contributions. Those will be revisited separately.
Highlights
AgentHostGitService(server-side) computes git state by shelling out to git; refreshed on session open and after each turn.SessionMetaChangedaction propagates_metadeltas without a full list refresh.AgentHostSessionAdapterretains_project/_workingDirectory/_metaso the workspace observable can be rebuilt when only_metachanges.baseBranchProtectedis computed client-side fromgit.branchProtectionconfig, so the workspace shape no longer carries it as a field onISessionRepository.update()only overwrites_metawhen the source actually provides one (SessionSummary feeds have no_metafield), so the polling refresh path no longer clobbers good state pushed viaSessionState.(Written by Copilot)