diff --git a/src/vs/base/common/marshallingIds.ts b/src/vs/base/common/marshallingIds.ts index c4b7fac045e..aec37b76d18 100644 --- a/src/vs/base/common/marshallingIds.ts +++ b/src/vs/base/common/marshallingIds.ts @@ -28,4 +28,5 @@ export const enum MarshalledId { LanguageModelPromptTsxPart, LanguageModelDataPart, ChatSessionContext, + ChatResponsePullRequestPart, } diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index a87dd454243..388dd4c34ff 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -251,6 +251,9 @@ const _allApiProposals = { languageModelSystem: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts', }, + languageModelToolResultAudience: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelToolResultAudience.d.ts', + }, languageStatusText: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatusText.d.ts', }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d1764383b45..81cb9edd77b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1855,6 +1855,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatResponseConfirmationPart: extHostTypes.ChatResponseConfirmationPart, ChatResponseMovePart: extHostTypes.ChatResponseMovePart, ChatResponseExtensionsPart: extHostTypes.ChatResponseExtensionsPart, + ChatResponsePullRequestPart: extHostTypes.ChatResponsePullRequestPart, ChatPrepareToolInvocationPart: extHostTypes.ChatPrepareToolInvocationPart, ChatResponseReferencePartStatusKind: extHostTypes.ChatResponseReferencePartStatusKind, ChatRequestTurn: extHostTypes.ChatRequestTurn, @@ -1873,11 +1874,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageModelToolResultPart: extHostTypes.LanguageModelToolResultPart, LanguageModelToolResultPart2: extHostTypes.LanguageModelToolResultPart2, LanguageModelTextPart: extHostTypes.LanguageModelTextPart, + LanguageModelTextPart2: extHostTypes.LanguageModelTextPart, + ToolResultAudience: extHostTypes.ToolResultAudience, LanguageModelToolCallPart: extHostTypes.LanguageModelToolCallPart, LanguageModelError: extHostTypes.LanguageModelError, LanguageModelToolResult: extHostTypes.LanguageModelToolResult, LanguageModelToolResult2: extHostTypes.LanguageModelToolResult2, LanguageModelDataPart: extHostTypes.LanguageModelDataPart, + LanguageModelDataPart2: extHostTypes.LanguageModelDataPart, LanguageModelToolExtensionSource: extHostTypes.LanguageModelToolExtensionSource, LanguageModelToolMCPSource: extHostTypes.LanguageModelToolMCPSource, ExtendedLanguageModelToolResult: extHostTypes.ExtendedLanguageModelToolResult, diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 400b71257f2..ea9cb670f14 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -291,6 +291,7 @@ export class ChatAgentResponseStream { part instanceof extHostTypes.ChatResponseCodeCitationPart || part instanceof extHostTypes.ChatResponseMovePart || part instanceof extHostTypes.ChatResponseExtensionsPart || + part instanceof extHostTypes.ChatResponsePullRequestPart || part instanceof extHostTypes.ChatResponseProgressPart2 ) { checkProposedApiEnabled(that._extension, 'chatParticipantAdditions'); diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index f4c7d462d08..4e886255ddd 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -99,7 +99,7 @@ class LanguageModelResponse { let out: vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart; if (fragment.part.type === 'text') { - out = new extHostTypes.LanguageModelTextPart(fragment.part.value); + out = new extHostTypes.LanguageModelTextPart(fragment.part.value, fragment.part.audience); } else if (fragment.part.type === 'data') { out = new extHostTypes.LanguageModelTextPart(''); } else { @@ -296,9 +296,9 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { if (fragment.part instanceof extHostTypes.LanguageModelToolCallPart) { part = { type: 'tool_use', name: fragment.part.name, parameters: fragment.part.input, toolCallId: fragment.part.callId }; } else if (fragment.part instanceof extHostTypes.LanguageModelTextPart) { - part = { type: 'text', value: fragment.part.value }; + part = { type: 'text', value: fragment.part.value, audience: fragment.part.audience }; } else if (fragment.part instanceof extHostTypes.LanguageModelDataPart) { - part = { type: 'data', value: { mimeType: fragment.part.mimeType as ChatImageMimeType, data: VSBuffer.wrap(fragment.part.data) } }; + part = { type: 'data', value: { mimeType: fragment.part.mimeType as ChatImageMimeType, data: VSBuffer.wrap(fragment.part.data) }, audience: fragment.part.audience }; } if (!part) { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index a64bd983416..8056f1ffaf1 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -41,7 +41,7 @@ import { IViewBadge } from '../../common/views.js'; import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js'; import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; import { IChatRequestVariableEntry, isImageVariableEntry } from '../../contrib/chat/common/chatVariableEntries.js'; -import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatPrepareToolInvocationPart, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; +import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatPrepareToolInvocationPart, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; import { IToolResult, IToolResultInputOutputDetails, IToolResultOutputDetails, ToolDataSource } from '../../contrib/chat/common/languageModelToolsService.js'; import * as chatProvider from '../../contrib/chat/common/languageModels.js'; import { IChatMessageDataPart, IChatResponseDataPart, IChatResponsePromptTsxPart, IChatResponseTextPart } from '../../contrib/chat/common/languageModels.js'; @@ -2316,11 +2316,11 @@ export namespace LanguageModelChatMessage { export function to(message: chatProvider.IChatMessage): vscode.LanguageModelChatMessage { const content = message.content.map(c => { if (c.type === 'text') { - return new LanguageModelTextPart(c.value); + return new LanguageModelTextPart(c.value, c.audience); } else if (c.type === 'tool_result') { const content: (LanguageModelTextPart | LanguageModelPromptTsxPart)[] = c.value.map(part => { if (part.type === 'text') { - return new types.LanguageModelTextPart(part.value); + return new types.LanguageModelTextPart(part.value, part.audience); } else { return new types.LanguageModelPromptTsxPart(part.value); } @@ -2360,7 +2360,8 @@ export namespace LanguageModelChatMessage { if (part instanceof types.LanguageModelTextPart) { return { type: 'text', - value: part.value + value: part.value, + audience: part.audience, } satisfies IChatResponseTextPart; } else if (part instanceof types.LanguageModelPromptTsxPart) { return { @@ -2411,11 +2412,11 @@ export namespace LanguageModelChatMessage2 { export function to(message: chatProvider.IChatMessage): vscode.LanguageModelChatMessage2 { const content = message.content.map(c => { if (c.type === 'text') { - return new LanguageModelTextPart(c.value); + return new LanguageModelTextPart(c.value, c.audience); } else if (c.type === 'tool_result') { const content: (LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart)[] = c.value.map(part => { if (part.type === 'text') { - return new types.LanguageModelTextPart(part.value); + return new types.LanguageModelTextPart(part.value, part.audience); } else if (part.type === 'data') { return new types.LanguageModelDataPart(part.value.data.buffer, part.value.mimeType); } else { @@ -2455,7 +2456,8 @@ export namespace LanguageModelChatMessage2 { if (part instanceof types.LanguageModelTextPart) { return { type: 'text', - value: part.value + value: part.value, + audience: part.audience, } satisfies IChatResponseTextPart; } else if (part instanceof types.LanguageModelPromptTsxPart) { return { @@ -2468,7 +2470,8 @@ export namespace LanguageModelChatMessage2 { value: { mimeType: part.mimeType as chatProvider.ChatImageMimeType, data: VSBuffer.wrap(part.data) - } + }, + audience: part.audience } satisfies IChatResponseDataPart; } else { // Strip unknown parts @@ -2493,6 +2496,7 @@ export namespace LanguageModelChatMessage2 { type: 'data', mimeType: c.mimeType, data: VSBuffer.wrap(c.data), + audience: c.audience } satisfies IChatMessageDataPart; } } else if (c instanceof types.LanguageModelToolCallPart) { @@ -2692,6 +2696,19 @@ export namespace ChatResponseExtensionsPart { } } +export namespace ChatResponsePullRequestPart { + export function from(part: vscode.ChatResponsePullRequestPart): Dto { + return { + kind: 'pullRequest', + author: part.author, + title: part.title, + description: part.description, + uri: part.uri, + linkTag: part.linkTag + }; + } +} + export namespace ChatResponseMovePart { export function from(part: vscode.ChatResponseMovePart): Dto { return { @@ -2982,6 +2999,8 @@ export namespace ChatResponsePart { return ChatResponseExtensionsPart.from(part); } else if (part instanceof types.ChatPrepareToolInvocationPart) { return ChatPrepareToolInvocationPart.from(part); + } else if (part instanceof types.ChatResponsePullRequestPart) { + return ChatResponsePullRequestPart.from(part); } else if (part instanceof types.ChatToolInvocationPart) { return ChatToolInvocationPart.from(part); } @@ -3215,7 +3234,7 @@ export namespace ChatAgentResult { if (value.$mid === MarshalledId.LanguageModelToolResult) { return new types.LanguageModelToolResult(cloneAndChange(value.content, reviveMetadata)); } else if (value.$mid === MarshalledId.LanguageModelTextPart) { - return new types.LanguageModelTextPart(value.value); + return new types.LanguageModelTextPart(value.value, value.audience); } else if (value.$mid === MarshalledId.LanguageModelPromptTsxPart) { return new types.LanguageModelPromptTsxPart(value.value); } @@ -3399,7 +3418,7 @@ export namespace LanguageModelToolResult { export function to(result: IToolResult): vscode.LanguageModelToolResult { return new types.LanguageModelToolResult(result.content.map(item => { if (item.kind === 'text') { - return new types.LanguageModelTextPart(item.value); + return new types.LanguageModelTextPart(item.value, item.audience); } else { return new types.LanguageModelPromptTsxPart(item.value); } @@ -3411,12 +3430,20 @@ export namespace LanguageModelToolResult { checkProposedApiEnabled(extension, 'chatParticipantPrivate'); } + const checkAudienceApi = (item: LanguageModelTextPart) => { + if (item.audience) { + checkProposedApiEnabled(extension, 'languageModelToolResultAudience'); + } + }; + return { content: result.content.map(item => { if (item instanceof types.LanguageModelTextPart) { + checkAudienceApi(item); return { kind: 'text', - value: item.value + value: item.value, + audience: item.audience }; } else if (item instanceof types.LanguageModelPromptTsxPart) { return { @@ -3437,13 +3464,9 @@ export namespace LanguageModelToolResult2 { export function to(result: IToolResult): vscode.LanguageModelToolResult2 { return new types.LanguageModelToolResult2(result.content.map(item => { if (item.kind === 'text') { - return new types.LanguageModelTextPart(item.value); + return new types.LanguageModelTextPart(item.value, item.audience); } else if (item.kind === 'data') { - const mimeType = Object.values(types.ChatImageMimeType).includes(item.value.mimeType as types.ChatImageMimeType) ? item.value.mimeType as types.ChatImageMimeType : undefined; - if (!mimeType) { - throw new Error('Invalid MIME type'); - } - return new types.LanguageModelDataPart(item.value.data.buffer, mimeType); + return new types.LanguageModelDataPart(item.value.data.buffer, item.value.mimeType, item.audience); } else { return new types.LanguageModelPromptTsxPart(item.value); } @@ -3455,6 +3478,12 @@ export namespace LanguageModelToolResult2 { checkProposedApiEnabled(extension, 'chatParticipantPrivate'); } + const checkAudienceApi = (item: LanguageModelTextPart | LanguageModelDataPart) => { + if (item.audience) { + checkProposedApiEnabled(extension, 'languageModelToolResultAudience'); + } + }; + let hasBuffers = false; let detailsDto: Dto | IToolResultInputOutputDetails | IToolResultOutputDetails | undefined> = undefined; if (Array.isArray(result.toolResultDetails)) { @@ -3477,9 +3506,11 @@ export namespace LanguageModelToolResult2 { const dto: Dto = { content: result.content.map(item => { if (item instanceof types.LanguageModelTextPart) { + checkAudienceApi(item); return { kind: 'text', - value: item.value + value: item.value, + audience: item.audience }; } else if (item instanceof types.LanguageModelPromptTsxPart) { return { @@ -3487,13 +3518,15 @@ export namespace LanguageModelToolResult2 { value: item.value, }; } else if (item instanceof types.LanguageModelDataPart) { + checkAudienceApi(item); hasBuffers = true; return { kind: 'data', value: { mimeType: item.mimeType, data: VSBuffer.wrap(item.data) - } + }, + audience: item.audience }; } else { throw new Error('Unknown LanguageModelToolResult part type'); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4e9688f4f95..f6a3d064b4a 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4703,6 +4703,27 @@ export class ChatResponseExtensionsPart { } } +export class ChatResponsePullRequestPart { + constructor( + public readonly uri: vscode.Uri, + public readonly title: string, + public readonly description: string, + public readonly author: string, + public readonly linkTag: string + ) { + } + + toJSON() { + return { + $mid: MarshalledId.ChatResponsePullRequestPart, + uri: this.uri, + title: this.title, + description: this.description, + author: this.author + }; + } +} + export class ChatResponseTextEditPart implements vscode.ChatResponseTextEditPart { uri: vscode.Uri; edits: vscode.TextEdit[]; @@ -4994,28 +5015,38 @@ export class LanguageModelToolCallPart implements vscode.LanguageModelToolCallPa } } -export class LanguageModelTextPart implements vscode.LanguageModelTextPart { - value: string; +export enum ToolResultAudience { + Assistant = 0, + User = 1, +} - constructor(value: string) { +export class LanguageModelTextPart implements vscode.LanguageModelTextPart2 { + value: string; + audience: vscode.ToolResultAudience[] | undefined; + + constructor(value: string, audience?: vscode.ToolResultAudience[]) { this.value = value; + audience = audience; } toJSON() { return { $mid: MarshalledId.LanguageModelTextPart, value: this.value, + audience: this.audience, }; } } -export class LanguageModelDataPart implements vscode.LanguageModelDataPart { +export class LanguageModelDataPart implements vscode.LanguageModelDataPart2 { mimeType: string; data: Uint8Array; + audience: vscode.ToolResultAudience[] | undefined; - constructor(data: Uint8Array, mimeType: string) { + constructor(data: Uint8Array, mimeType: string, audience?: vscode.ToolResultAudience[]) { this.mimeType = mimeType; this.data = data; + this.audience = audience; } static image(data: Uint8Array, mimeType: ChatImageMimeType): vscode.LanguageModelDataPart { @@ -5036,6 +5067,7 @@ export class LanguageModelDataPart implements vscode.LanguageModelDataPart { $mid: MarshalledId.LanguageModelDataPart, mimeType: this.mimeType, data: this.data, + audience: this.audience }; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatPullRequestContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatPullRequestContentPart.ts new file mode 100644 index 00000000000..1179c514f48 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatPullRequestContentPart.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import './media/chatPullRequestContent.css'; +import * as dom from '../../../../../base/browser/dom.js'; +import { Emitter } from '../../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { IChatPullRequestContent } from '../../common/chatService.js'; +import { IChatRendererContent } from '../../common/chatViewModel.js'; +import { ChatTreeItem } from '../chat.js'; +import { IChatContentPart } from './chatContentParts.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; +import { ThemeIcon } from '../../../../../base/common/themables.js'; +import { localize } from '../../../../../nls.js'; + +export class ChatPullRequestContentPart extends Disposable implements IChatContentPart { + public readonly domNode: HTMLElement; + + private _onDidChangeHeight = this._register(new Emitter()); + public readonly onDidChangeHeight = this._onDidChangeHeight.event; + + constructor( + private readonly pullRequestContent: IChatPullRequestContent + ) { + super(); + + this.domNode = dom.$('.chat-pull-request-content-part'); + const container = dom.append(this.domNode, dom.$('.container')); + const icon = dom.append(container, dom.$('.icon')); + const contentContainer = dom.append(container, dom.$('.content-container')); + + const titleContainer = dom.append(contentContainer, dom.$('p.title-container')); + icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.gitPullRequest)); + const titleElement = dom.append(titleContainer, dom.$('.title')); + titleElement.textContent = this.pullRequestContent.title; + const linkElement: HTMLAnchorElement = dom.append(titleContainer, dom.$('a.link')); + linkElement.textContent = this.pullRequestContent.linkTag; + linkElement.href = this.pullRequestContent.uri.toString(); + + const metaElement = dom.append(contentContainer, dom.$('.meta')); + const authorElement = dom.append(metaElement, dom.$('.author')); + authorElement.textContent = localize('chatPullRequest.author', 'by {0}', this.pullRequestContent.author); + + const descriptionElement = dom.append(contentContainer, dom.$('.description')); + descriptionElement.textContent = this.pullRequestContent.description; + } + + hasSameContent(other: IChatRendererContent, followingContent: IChatRendererContent[], element: ChatTreeItem): boolean { + return other.kind === 'pullRequest'; + } + + addDisposable(disposable: IDisposable): void { + this._register(disposable); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatPullRequestContent.css b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatPullRequestContent.css new file mode 100644 index 00000000000..ce74a313eee --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatPullRequestContent.css @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.chat-pull-request-content-part { + border: 1px solid var(--vscode-chat-requestBorder); + border-radius: 4px; +} + +.chat-pull-request-content-part .container { + padding: 16px 16px 8px; + display: flex; + flex-direction: row; +} + +.chat-pull-request-content-part .container .icon { + padding-right: 4px; + padding-top: 2px; +} + +.chat-pull-request-content-part .content-container { + display: flex; + flex-direction: column; +} + +.chat-pull-request-content-part .title-container { + flex-direction: row; + display: flex; + gap: 4px; + font-size: large; + padding-bottom: 10px; + margin: unset; +} + +.chat-pull-request-content-part .title-container .link { + color: var(--vscode-textLink-foreground); +} + +.chat-pull-request-content-part .title-container .icon.codicon { + font-size: large; +} + +.chat-pull-request-content-part .description { + display: -webkit-box; + line-clamp: 2; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + position: relative; +} + +.chat-pull-request-content-part .description::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 4em; + /* Adjust as needed */ + pointer-events: none; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0), var(--vscode-sideBar-background)); + +} diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 9416525db60..6179814176f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -49,7 +49,7 @@ import { IChatAgentMetadata } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { IChatTextEditGroup } from '../common/chatModel.js'; import { chatSubcommandLeader } from '../common/chatParserTypes.js'; -import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatErrorLevel, IChatChangesSummary, IChatConfirmation, IChatContentReference, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatTask, IChatTaskSerialized, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop } from '../common/chatService.js'; +import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatErrorLevel, IChatChangesSummary, IChatConfirmation, IChatContentReference, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatPullRequestContent, IChatTask, IChatTaskSerialized, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop } from '../common/chatService.js'; import { IChatChangesSummaryPart, IChatCodeCitations, IChatErrorDetailsPart, IChatReferences, IChatRendererContent, IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatWorkingProgress, isRequestVM, isResponseVM } from '../common/chatViewModel.js'; import { getNWords } from '../common/chatWordCounter.js'; import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js'; @@ -83,6 +83,7 @@ import { IChatRequestVariableEntry } from '../common/chatVariableEntries.js'; import { ChatElicitationContentPart } from './chatContentParts/chatElicitationContentPart.js'; import { alert } from '../../../../base/browser/ui/aria/aria.js'; import { CodiconActionViewItem } from '../../notebook/browser/view/cellParts/cellActionView.js'; +import { ChatPullRequestContentPart } from './chatContentParts/chatPullRequestContentPart.js'; import { ChatCheckpointFileChangesSummaryContentPart } from './chatContentParts/chatChangesSummaryPart.js'; const $ = dom.$; @@ -1124,6 +1125,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this.updateItemHeight(templateData))); + return part; + } + private renderProgressTask(task: IChatTask | IChatTaskSerialized, templateData: IChatListItemTemplate, context: IChatContentPartRenderContext): IChatContentPart | undefined { if (!isResponseVM(context.element)) { return; diff --git a/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts b/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts index 181f93c9748..658b8d9a3fb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts +++ b/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts @@ -93,7 +93,9 @@ class ChatResponseAccessibleProvider extends Disposable implements IAccessibleVi ? JSON.stringify(toolInvocation.toolSpecificData.extensions) : toolInvocation.toolSpecificData?.kind === 'tasks' ? JSON.stringify(toolInvocation.toolSpecificData.tasks) - : JSON.stringify(toolInvocation.toolSpecificData.rawInput); + : toolInvocation.toolSpecificData?.kind === 'pullRequest' + ? JSON.stringify(toolInvocation.toolSpecificData) + : JSON.stringify(toolInvocation.toolSpecificData.rawInput); } responseContent += `${title}`; if (input) { diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 12ceb30c0f9..dc9ee6f9269 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -26,7 +26,7 @@ import { CellUri, ICellEditOperation } from '../../notebook/common/notebookCommo import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, reviveSerializedAgent } from './chatAgents.js'; import { IChatEditingService, IChatEditingSession } from './chatEditingService.js'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js'; -import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatNotebookEdit, IChatPrepareToolInvocationPart, IChatProgress, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext } from './chatService.js'; +import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatNotebookEdit, IChatPrepareToolInvocationPart, IChatProgress, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext } from './chatService.js'; import { IChatRequestVariableEntry } from './chatVariableEntries.js'; import { ChatAgentLocation, ChatModeKind } from './constants.js'; @@ -116,7 +116,8 @@ export type IChatProgressHistoryResponseContent = | IChatTextEditGroup | IChatNotebookEditGroup | IChatConfirmation - | IChatExtensionsContent; + | IChatExtensionsContent + | IChatPullRequestContent; /** * "Normal" progress kinds that are rendered as parts of the stream of content. @@ -354,6 +355,7 @@ class AbstractResponse implements IResponse { case 'toolInvocation': case 'toolInvocationSerialized': case 'extensions': + case 'pullRequest': case 'undoStop': case 'prepareToolInvocation': case 'elicitation': diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 9749c1dfaf6..ba5896b98fd 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -265,7 +265,7 @@ export interface IChatToolInputInvocationData { export interface IChatToolInvocation { presentation: IPreparedToolInvocation['presentation']; - toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTasksContent; + toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTasksContent; /** Presence of this property says that confirmation is required */ confirmationMessages?: IToolConfirmationMessages; confirmed: DeferredPromise; @@ -298,7 +298,7 @@ export interface IToolResultOutputDetailsSerialized { */ export interface IChatToolInvocationSerialized { presentation: IPreparedToolInvocation['presentation']; - toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTasksContent; + toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTasksContent; invocationMessage: string | IMarkdownString; originMessage: string | IMarkdownString | undefined; pastTenseMessage: string | IMarkdownString | undefined; @@ -315,6 +315,15 @@ export interface IChatExtensionsContent { kind: 'extensions'; } +export interface IChatPullRequestContent { + uri: URI; + title: string; + description: string; + author: string; + linkTag: string; + kind: 'pullRequest'; +} + export interface IChatTasksContent { kind: 'tasks'; sessionId: string; @@ -352,6 +361,7 @@ export type IChatProgress = | IChatToolInvocation | IChatToolInvocationSerialized | IChatExtensionsContent + | IChatPullRequestContent | IChatUndoStop | IChatPrepareToolInvocationPart | IChatTaskSerialized diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 2f00490ecc7..33e620fb979 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -22,6 +22,7 @@ import { VSBuffer } from '../../../../base/common/buffer.js'; import { derived, IObservable, IReader, ITransaction, ObservableSet } from '../../../../base/common/observable.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { localize } from '../../../../nls.js'; +import { ToolResultAudience } from './languageModels.js'; export interface IToolData { id: string; @@ -191,6 +192,7 @@ export function stringifyPromptTsxPart(part: IToolResultPromptTsxPart): string { export interface IToolResultTextPart { kind: 'text'; value: string; + audience?: ToolResultAudience[]; } export interface IToolResultDataPart { @@ -199,6 +201,7 @@ export interface IToolResultDataPart { mimeType: string; data: VSBuffer; }; + audience?: ToolResultAudience[]; } export interface IToolConfirmationMessages { diff --git a/src/vs/workbench/contrib/chat/common/languageModels.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts index ec5c6a9373c..3346e8fd95a 100644 --- a/src/vs/workbench/contrib/chat/common/languageModels.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -10,6 +10,7 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { isFalsyOrWhitespace } from '../../../../base/common/strings.js'; +import { URI } from '../../../../base/common/uri.js'; import { localize } from '../../../../nls.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; @@ -24,9 +25,15 @@ export const enum ChatMessageRole { Assistant, } +export enum ToolResultAudience { + Assistant = 0, + User = 1, +} + export interface IChatMessageTextPart { type: 'text'; value: string; + audience?: ToolResultAudience[]; } export interface IChatMessageImagePart { @@ -38,6 +45,7 @@ export interface IChatMessageDataPart { type: 'data'; mimeType: string; data: VSBuffer; + audience?: ToolResultAudience[]; } export interface IChatImageURLPart { @@ -90,6 +98,7 @@ export interface IChatMessage { export interface IChatResponseTextPart { type: 'text'; value: string; + audience?: ToolResultAudience[]; } export interface IChatResponsePromptTsxPart { @@ -100,6 +109,7 @@ export interface IChatResponsePromptTsxPart { export interface IChatResponseDataPart { type: 'data'; value: IChatImageURLPart; + audience?: ToolResultAudience[]; } export interface IChatResponseToolUsePart { @@ -109,6 +119,17 @@ export interface IChatResponseToolUsePart { parameters: any; } +export interface IChatResponsePullRequestPart { + type: 'pullRequest'; + uri: URI; + title: string; + description: string; + author: string; + linkTag: string; +} + +export type IExtendedChatResponsePart = IChatResponsePullRequestPart; + export type IChatResponsePart = IChatResponseTextPart | IChatResponseToolUsePart | IChatResponseDataPart; export interface IChatResponseFragment { diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index 78460d67f07..fc616ac275c 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -107,7 +107,7 @@ declare module 'vscode' { constructor(toolName: string, toolCallId: string, isError?: boolean); } - export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseNotebookEditPart | ChatResponseConfirmationPart | ChatResponseCodeCitationPart | ChatResponseReferencePart2 | ChatResponseMovePart | ChatResponseExtensionsPart | ChatPrepareToolInvocationPart | ChatToolInvocationPart; + export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseNotebookEditPart | ChatResponseConfirmationPart | ChatResponseCodeCitationPart | ChatResponseReferencePart2 | ChatResponseMovePart | ChatResponseExtensionsPart | ChatResponsePullRequestPart | ChatPrepareToolInvocationPart | ChatToolInvocationPart; export class ChatResponseWarningPart { value: MarkdownString; constructor(value: string | MarkdownString); @@ -193,6 +193,15 @@ declare module 'vscode' { constructor(extensions: string[]); } + export class ChatResponsePullRequestPart { + readonly uri: Uri; + readonly linkTag: string; + readonly title: string; + readonly description: string; + readonly author: string; + constructor(uri: Uri, title: string, description: string, author: string, linkTag: string); + } + export interface ChatResponseStream { /** diff --git a/src/vscode-dts/vscode.proposed.languageModelToolResultAudience.d.ts b/src/vscode-dts/vscode.proposed.languageModelToolResultAudience.d.ts new file mode 100644 index 00000000000..0b9ef350389 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.languageModelToolResultAudience.d.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export enum ToolResultAudience { + Assistant = 0, + User = 1, + } + + /** + * A language model response part containing a piece of text, returned from a {@link LanguageModelChatResponse}. + */ + export class LanguageModelTextPart2 extends LanguageModelTextPart { + audience: ToolResultAudience[] | undefined; + constructor(value: string, audience?: ToolResultAudience[]); + } + + export class LanguageModelDataPart2 extends LanguageModelDataPart { + audience: ToolResultAudience[] | undefined; + constructor(data: Uint8Array, mimeType: string, audience?: ToolResultAudience[]); + } +}