diff --git a/src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts b/src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts index 9751c10d8f30e..10b359d1611db 100644 --- a/src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts +++ b/src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts @@ -32,7 +32,7 @@ import { diffsEqual, diffsToChanges, mapProtocolStatus } from './agentHostDiffs. import { buildMutableConfigSchema, IAgentHostSessionsProvider, resolvedConfigsEqual } from '../../../common/agentHostSessionsProvider.js'; import { agentHostSessionWorkspaceKey } from '../../../common/agentHostSessionWorkspace.js'; import { isSessionConfigComplete } from '../../../common/sessionConfig.js'; -import { IChat, IGitHubInfo, ISession, ISessionType, ISessionWorkspace, ISessionWorkspaceBrowseAction, SessionStatus, toSessionId } from '../../../services/sessions/common/session.js'; +import { IChat, IGitHubInfo, ISession, ISessionChangeset, ISessionType, ISessionWorkspace, ISessionWorkspaceBrowseAction, SessionStatus, toSessionId } from '../../../services/sessions/common/session.js'; import { ISendRequestOptions, ISessionChangeEvent } from '../../../services/sessions/common/sessionsProvider.js'; // ============================================================================ @@ -74,6 +74,7 @@ export class AgentHostSessionAdapter implements ISession { readonly updatedAt: ISettableObservable; readonly status: ISettableObservable; readonly changes = observableValue('changes', []); + readonly changesets = observableValue('changesets', []); readonly modelId: ISettableObservable; modelSelection: ModelSelection | undefined; readonly mode = observableValue<{ readonly id: string; readonly kind: string } | undefined>('mode', undefined); @@ -162,6 +163,7 @@ export class AgentHostSessionAdapter implements ISession { updatedAt: this.updatedAt, status: this.status, changes: this.changes, + changesets: this.changesets, modelId: this.modelId, mode: this.mode, isArchived: this.isArchived, @@ -381,6 +383,7 @@ class NewSession extends Disposable { this._status = observableValue(this, SessionStatus.Untitled); const title = observableValue(this, ''); const updatedAt = observableValue(this, new Date()); + const changesets = observableValue(this, []); const changes = observableValue(this, []); this._modelId = observableValue(this, undefined); const mode = observableValue<{ readonly id: string; readonly kind: string } | undefined>(this, undefined); @@ -394,6 +397,7 @@ class NewSession extends Disposable { const mainChat: IChat = { resource, createdAt, title, updatedAt, status: this._status, + changesets, changes, modelId: this._modelId, mode, isArchived, isRead, description, lastTurnEnd, @@ -411,6 +415,7 @@ class NewSession extends Disposable { title, updatedAt, status: this._status, + changesets, changes, modelId: this._modelId, mode, diff --git a/src/vs/sessions/contrib/agentHost/test/browser/agentHostSkillButtons.test.ts b/src/vs/sessions/contrib/agentHost/test/browser/agentHostSkillButtons.test.ts index 8bd40afd259c1..262afad7d3d28 100644 --- a/src/vs/sessions/contrib/agentHost/test/browser/agentHostSkillButtons.test.ts +++ b/src/vs/sessions/contrib/agentHost/test/browser/agentHostSkillButtons.test.ts @@ -31,6 +31,7 @@ function makeActiveSession(providerId: string): IActiveSession { title: observableValue('t', 'Test'), updatedAt: observableValue('u', new Date()), status: observableValue('s', 0), + changesets: observableValue('cs', []), changes: observableValue('c', []), modelId: observableValue('m', undefined), mode: observableValue('mo', undefined), @@ -50,6 +51,7 @@ function makeActiveSession(providerId: string): IActiveSession { title: chat.title, updatedAt: chat.updatedAt, status: chat.status, + changesets: chat.changesets, changes: chat.changes, modelId: chat.modelId, mode: chat.mode, diff --git a/src/vs/sessions/contrib/chat/test/browser/sessionsConfigurationService.test.ts b/src/vs/sessions/contrib/chat/test/browser/sessionsConfigurationService.test.ts index 0d691444e24fd..7c502613ab65f 100644 --- a/src/vs/sessions/contrib/chat/test/browser/sessionsConfigurationService.test.ts +++ b/src/vs/sessions/contrib/chat/test/browser/sessionsConfigurationService.test.ts @@ -41,6 +41,7 @@ function makeSession(opts: { repository?: URI; worktree?: URI } = {}): ISession title: observableValue('title', 'session'), updatedAt: observableValue('updatedAt', new Date()), status: observableValue('status', SessionStatus.Untitled), + changesets: observableValue('changesets', []), changes: observableValue('changes', []), modelId: observableValue('modelId', undefined), mode: observableValue('mode', undefined), @@ -60,6 +61,7 @@ function makeSession(opts: { repository?: URI; worktree?: URI } = {}): ISession title: chat.title, updatedAt: chat.updatedAt, status: chat.status, + changesets: chat.changesets, changes: chat.changes, modelId: chat.modelId, mode: chat.mode, diff --git a/src/vs/sessions/contrib/copilotChatSessions/browser/copilotChatSessionsProvider.ts b/src/vs/sessions/contrib/copilotChatSessions/browser/copilotChatSessionsProvider.ts index 8bc493b53ba41..3cb428c22977a 100644 --- a/src/vs/sessions/contrib/copilotChatSessions/browser/copilotChatSessionsProvider.ts +++ b/src/vs/sessions/contrib/copilotChatSessions/browser/copilotChatSessionsProvider.ts @@ -23,7 +23,7 @@ import { AgentSessionProviders, AgentSessionTarget } from '../../../../workbench import { IChatService, IChatSendRequestOptions } from '../../../../workbench/contrib/chat/common/chatService/chatService.js'; import { IChatResponseModel } from '../../../../workbench/contrib/chat/common/model/chatModel.js'; import { ChatSessionStatus, IChatSessionsService, IChatSessionProviderOptionGroup, IChatSessionProviderOptionItem, SessionType } from '../../../../workbench/contrib/chat/common/chatSessionsService.js'; -import { ISession, IChat, ISessionRepository, ISessionWorkspace, SessionStatus, GITHUB_REMOTE_FILE_SCHEME, IGitHubInfo, CopilotCLISessionType, CopilotCloudSessionType, ClaudeCodeSessionType, ISessionType, ISessionWorkspaceBrowseAction, ISessionFileChange, toSessionId, SESSION_WORKSPACE_GROUP_LOCAL } from '../../../services/sessions/common/session.js'; +import { ISession, IChat, ISessionRepository, ISessionWorkspace, SessionStatus, GITHUB_REMOTE_FILE_SCHEME, IGitHubInfo, CopilotCLISessionType, CopilotCloudSessionType, ClaudeCodeSessionType, ISessionType, ISessionWorkspaceBrowseAction, ISessionFileChange, toSessionId, SESSION_WORKSPACE_GROUP_LOCAL, ISessionChangeset } from '../../../services/sessions/common/session.js'; import { ChatAgentLocation, ChatConfiguration, ChatModeKind, ChatPermissionLevel, isChatPermissionLevel } from '../../../../workbench/contrib/chat/common/constants.js'; import { basename, dirname, isEqual } from '../../../../base/common/resources.js'; import { ISendRequestOptions, ISessionChangeEvent, ISessionsProvider } from '../../../services/sessions/common/sessionsProvider.js'; @@ -71,6 +71,8 @@ export interface ICopilotChatSession { readonly updatedAt: IObservable; /** Current session status. */ readonly status: IObservable; + /** File changesets produced by the session. */ + readonly changesets: IObservable; /** File changes produced by the session. */ readonly changes: IObservable; /** Currently selected model identifier. */ @@ -180,6 +182,9 @@ class CopilotCLISession extends Disposable implements ICopilotChatSession { private readonly _loading = observableValue(this, true); readonly loading: IObservable = this._loading; + private readonly _changesets: ReturnType>; + readonly changesets: IObservable; + private readonly _changes: ReturnType>; readonly changes: IObservable; @@ -259,6 +264,9 @@ class CopilotCLISession extends Disposable implements ICopilotChatSession { this._description = observableValue(this, undefined); this.description = this._description; + this._changesets = observableValue(this, []); + this.changesets = this._changesets; + this._changes = observableValue(this, []); this.changes = this._changes; } @@ -416,6 +424,7 @@ class CopilotCLISession extends Disposable implements ICopilotChatSession { this._title.set(session.title.get(), undefined); this._status.set(session.status.get(), undefined); this._updatedAt.set(session.updatedAt.get(), undefined); + this._changesets.set(session.changesets.get(), undefined); this._changes.set(session.changes.get(), undefined); this._description.set(session.description.get(), undefined); } @@ -463,6 +472,7 @@ export class RemoteNewSession extends Disposable implements ICopilotChatSession private readonly _workspaceData = observableValue(this, undefined); readonly workspace: IObservable = this._workspaceData; + readonly changesets: IObservable = observableValue(this, []); readonly changes: IObservable = observableValue(this, []); private readonly _modelIdObservable = observableValue(this, undefined); @@ -705,6 +715,7 @@ class ClaudeCodeNewSession extends Disposable implements ICopilotChatSession { private readonly _workspaceData = observableValue(this, undefined); readonly workspace: IObservable = this._workspaceData; + readonly changesets: IObservable = observableValue(this, []); readonly changes: IObservable = observableValue(this, []); private readonly _modelIdObservable = observableValue(this, undefined); @@ -844,6 +855,9 @@ class AgentSessionAdapter implements ICopilotChatSession { private readonly _status: ReturnType>; readonly status: IObservable; + private readonly _changesets: ReturnType>; + readonly changesets: IObservable; + private readonly _changes: ReturnType>; readonly changes: IObservable; @@ -896,6 +910,9 @@ class AgentSessionAdapter implements ICopilotChatSession { this._status = observableValue(this, toSessionStatus(session.status)); this.status = this._status; + this._changesets = observableValue(this, this._extractChangesets(session)); + this.changesets = this._changesets; + this._changes = observableValue(this, this._extractChanges(session)); this.changes = this._changes; @@ -1094,6 +1111,10 @@ class AgentSessionAdapter implements ICopilotChatSession { return undefined; } + private _extractChangesets(session: IAgentSession): readonly ISessionChangeset[] { + return []; + } + private _extractChanges(session: IAgentSession): readonly ISessionFileChange[] { if (!session.changes) { return []; @@ -2593,6 +2614,7 @@ export class CopilotChatSessionsProvider extends Disposable implements ISessions title: primaryChat.title, updatedAt: chatsObs.map((chats, reader) => this._latestDate(chats, c => c.updatedAt.read(reader))!), status: chatsObs.map((chats, reader) => this._aggregateStatus(chats, reader)), + changesets: primaryChat.changesets, changes: primaryChat.changes, modelId: primaryChat.modelId, mode: primaryChat.mode, @@ -2623,6 +2645,7 @@ export class CopilotChatSessionsProvider extends Disposable implements ISessions title: chat.title, updatedAt: chat.updatedAt, status: chat.status, + changesets: chat.changesets, changes: chat.changes, modelId: chat.modelId, mode: chat.mode, @@ -2645,6 +2668,7 @@ export class CopilotChatSessionsProvider extends Disposable implements ISessions title: chat.title, updatedAt: chat.updatedAt, status: chat.status, + changesets: chat.changesets, changes: chat.changes, modelId: chat.modelId, mode: chat.mode, diff --git a/src/vs/sessions/contrib/github/test/browser/githubContribution.test.ts b/src/vs/sessions/contrib/github/test/browser/githubContribution.test.ts index 0973ae476a876..a6d58137b5d45 100644 --- a/src/vs/sessions/contrib/github/test/browser/githubContribution.test.ts +++ b/src/vs/sessions/contrib/github/test/browser/githubContribution.test.ts @@ -15,7 +15,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes import { mock } from '../../../../../base/test/common/mock.js'; import { GitHubPullRequestPollingContribution } from '../../browser/github.contribution.js'; import { IGitHubService } from '../../browser/githubService.js'; -import { IChat, IGitHubInfo, ISession, ISessionCapabilities, ISessionFileChange, ISessionWorkspace, SessionStatus } from '../../../../services/sessions/common/session.js'; +import { IChat, IGitHubInfo, ISession, ISessionCapabilities, ISessionChangeset, ISessionFileChange, ISessionWorkspace, SessionStatus } from '../../../../services/sessions/common/session.js'; import { IActiveSession, ISessionsChangeEvent, ISessionsManagementService } from '../../../../services/sessions/common/sessionsManagement.js'; suite('GitHubPullRequestPollingContribution', () => { @@ -152,6 +152,7 @@ class TestSession implements ISession { readonly title: ReturnType>; readonly updatedAt: ReturnType>; readonly status: ReturnType>; + readonly changesets: ReturnType>; readonly changes: ReturnType>; readonly workspace: ReturnType>; readonly modelId: ReturnType>; @@ -172,6 +173,7 @@ class TestSession implements ISession { this.title = observableValue(`test.title.${id}`, id); this.updatedAt = observableValue(`test.updatedAt.${id}`, new Date(0)); this.status = observableValue(`test.status.${id}`, SessionStatus.Completed); + this.changesets = observableValue(`test.changesets.${id}`, []); this.changes = observableValue(`test.changes.${id}`, []); this.workspace = observableValue(`test.workspace.${id}`, undefined); this.modelId = observableValue(`test.modelId.${id}`, undefined); @@ -188,6 +190,7 @@ class TestSession implements ISession { title: this.title, updatedAt: this.updatedAt, status: this.status, + changesets: this.changesets, changes: this.changes, modelId: this.modelId, mode: this.mode, diff --git a/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts b/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts index abe2301a9ae47..89088f69af82e 100644 --- a/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts +++ b/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts @@ -35,6 +35,7 @@ function createSession(id: string, opts: { title: observableValue(`title-${id}`, id), updatedAt: observableValue(`updatedAt-${id}`, updatedAt), status: observableValue(`status-${id}`, SessionStatus.Completed), + changesets: observableValue(`changesets-${id}`, []), changes: observableValue(`changes-${id}`, []), modelId: observableValue(`modelId-${id}`, undefined), mode: observableValue(`mode-${id}`, undefined), diff --git a/src/vs/sessions/contrib/sessions/test/browser/sessionsListModelService.test.ts b/src/vs/sessions/contrib/sessions/test/browser/sessionsListModelService.test.ts index 1f1210595bbbd..04a7af7656872 100644 --- a/src/vs/sessions/contrib/sessions/test/browser/sessionsListModelService.test.ts +++ b/src/vs/sessions/contrib/sessions/test/browser/sessionsListModelService.test.ts @@ -28,6 +28,7 @@ function createSession(id: string): ISession { title: observableValue(`title-${id}`, id), updatedAt: observableValue(`updatedAt-${id}`, new Date()), status: observableValue(`status-${id}`, SessionStatus.Completed), + changesets: observableValue(`changesets-${id}`, []), changes: observableValue(`changes-${id}`, []), modelId: observableValue(`modelId-${id}`, undefined), mode: observableValue(`mode-${id}`, undefined), diff --git a/src/vs/sessions/contrib/terminal/test/browser/sessionsTerminalContribution.test.ts b/src/vs/sessions/contrib/terminal/test/browser/sessionsTerminalContribution.test.ts index f725fe4177e2d..94ffb04868831 100644 --- a/src/vs/sessions/contrib/terminal/test/browser/sessionsTerminalContribution.test.ts +++ b/src/vs/sessions/contrib/terminal/test/browser/sessionsTerminalContribution.test.ts @@ -64,6 +64,7 @@ function makeAgentSession(opts: { title: observableValue('test.title', 'Test Session'), updatedAt: observableValue('test.updatedAt', new Date()), status: observableValue('test.status', 0), + changesets: observableValue('test.changesets', []), changes: observableValue('test.changes', []), modelId: observableValue('test.modelId', undefined), mode: observableValue('test.mode', undefined), @@ -83,6 +84,7 @@ function makeAgentSession(opts: { title: chat.title, updatedAt: chat.updatedAt, status: chat.status, + changesets: chat.changesets, changes: chat.changes, modelId: chat.modelId, mode: chat.mode, @@ -113,6 +115,7 @@ function makeNonAgentSession(opts: { repository?: URI; worktree?: URI; providerT title: observableValue('test.title', 'Test Session'), updatedAt: observableValue('test.updatedAt', new Date()), status: observableValue('test.status', 0), + changesets: observableValue('test.changesets', []), changes: observableValue('test.changes', []), modelId: observableValue('test.modelId', undefined), mode: observableValue('test.mode', undefined), @@ -132,6 +135,7 @@ function makeNonAgentSession(opts: { repository?: URI; worktree?: URI; providerT title: chat.title, updatedAt: chat.updatedAt, status: chat.status, + changesets: chat.changesets, changes: chat.changes, modelId: chat.modelId, mode: chat.mode, diff --git a/src/vs/sessions/services/sessions/common/session.ts b/src/vs/sessions/services/sessions/common/session.ts index 0f348c9e9e31a..942186d8f9e7b 100644 --- a/src/vs/sessions/services/sessions/common/session.ts +++ b/src/vs/sessions/services/sessions/common/session.ts @@ -150,6 +150,19 @@ export interface IGitHubInfo { export type ISessionFileChange = IChatSessionFileChange | IChatSessionFileChange2; +export interface ISessionChangeset { + /** Unique identifier for the changeset. */ + readonly id: string; + /** Display label for the changeset. */ + readonly label: string; + /** Optional description for the changeset. */ + readonly description?: string; + /** Whether the changeset is enabled. */ + readonly enabled: IObservable; + /** File changes associated with this changeset. */ + readonly changes: IObservable; +} + /** * A single chat within a session, produced by the sessions management layer. */ @@ -169,6 +182,8 @@ export interface IChat { readonly status: IObservable; /** File changes produced by the chat. */ readonly changes: IObservable; + /** Changesets produced by the chat. */ + readonly changesets: IObservable; /** Currently selected model identifier. */ readonly modelId: IObservable; /** Currently selected mode identifier and kind. */ @@ -213,6 +228,8 @@ export interface ISession { readonly status: IObservable; /** File changes produced by the session. */ readonly changes: IObservable; + /** Changesets produced by the session. */ + readonly changesets: IObservable; /** Currently selected model identifier. */ readonly modelId: IObservable; /** Currently selected mode identifier and kind. */ diff --git a/src/vs/sessions/services/sessions/test/browser/sessionsManagementService.test.ts b/src/vs/sessions/services/sessions/test/browser/sessionsManagementService.test.ts index 62d243e05a9e2..39e77d8788885 100644 --- a/src/vs/sessions/services/sessions/test/browser/sessionsManagementService.test.ts +++ b/src/vs/sessions/services/sessions/test/browser/sessionsManagementService.test.ts @@ -18,6 +18,7 @@ const stubChat: IChat = { title: constObservable('Chat'), updatedAt: constObservable(new Date()), status: constObservable(0), + changesets: constObservable([]), changes: constObservable([]), modelId: constObservable(undefined), mode: constObservable(undefined), @@ -37,6 +38,7 @@ function stubSession(overrides: Partial & Pick