diff --git a/src/@types/vscode.proposed.chatSessionsProvider.d.ts b/src/@types/vscode.proposed.chatSessionsProvider.d.ts index 48d8f12e6c..e409bbc176 100644 --- a/src/@types/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/@types/vscode.proposed.chatSessionsProvider.d.ts @@ -31,9 +31,8 @@ declare module 'vscode' { /** * Provides a list of chat sessions. - * - * TODO: Do we need a flag to try auth if needed? */ + // TODO: Do we need a flag to try auth if needed? provideChatSessionItems(token: CancellationToken): ProviderResult; } @@ -60,10 +59,9 @@ declare module 'vscode' { * The full history of the session * * This should not include any currently active responses - * - * TODO: Are these the right types to use? - * TODO: link request + response to encourage correct usage? */ + // TODO: Are these the right types to use? + // TODO: link request + response to encourage correct usage? readonly history: ReadonlyArray; /** @@ -79,9 +77,8 @@ declare module 'vscode' { * Handles new request for the session. * * If not set, then the session will be considered read-only and no requests can be made. - * - * TODO: Should we introduce our own type for `ChatRequestHandler` since not all field apply to chat sessions? */ + // TODO: Should we introduce our own type for `ChatRequestHandler` since not all field apply to chat sessions? readonly requestHandler: ChatRequestHandler | undefined; } @@ -89,17 +86,32 @@ declare module 'vscode' { /** * Resolves a chat session into a full `ChatSession` object. * - * @param uri The URI of the chat session to open. Uris as structured as `vscode-chat-session:/id` + * @param sessionId The id of the chat session to open. * @param token A cancellation token that can be used to cancel the operation. */ - provideChatSessionContent(id: string, token: CancellationToken): Thenable; + provideChatSessionContent(sessionId: string, token: CancellationToken): Thenable | ChatSession; } export namespace chat { + /** + * Registers a new {@link ChatSessionItemProvider chat session item provider}. + * + * To use this, also make sure to also add `chatSessions` contribution in the `package.json`. + * + * @param chatSessionType The type of chat session the provider is for. + * @param provider The provider to register. + * + * @returns A disposable that unregisters the provider when disposed. + */ export function registerChatSessionItemProvider(chatSessionType: string, provider: ChatSessionItemProvider): Disposable; /** + * Registers a new {@link ChatSessionContentProvider chat session content provider}. + * * @param chatSessionType A unique identifier for the chat session type. This is used to differentiate between different chat session providers. + * @param provider The provider to register. + * + * @returns A disposable that unregisters the provider when disposed. */ export function registerChatSessionContentProvider(chatSessionType: string, provider: ChatSessionContentProvider): Disposable; } @@ -114,7 +126,9 @@ declare module 'vscode' { } export namespace window { - - export function showChatSession(chatSessionType: string, id: string, options: ChatSessionShowOptions): Thenable; + /** + * Shows a chat session in the panel or editor. + */ + export function showChatSession(chatSessionType: string, sessionId: string, options: ChatSessionShowOptions): Thenable; } } diff --git a/src/lm/tools/activePullRequestTool.ts b/src/lm/tools/activePullRequestTool.ts index 6a64007645..9ad0be7f8b 100644 --- a/src/lm/tools/activePullRequestTool.ts +++ b/src/lm/tools/activePullRequestTool.ts @@ -5,6 +5,7 @@ 'use strict'; import * as vscode from 'vscode'; +import { parseSessionLogs } from '../../../common/sessionParsing'; import { COPILOT_LOGINS } from '../../common/copilot'; import { GitChangeType, InMemFileChange } from '../../common/file'; import Logger from '../../common/logger'; @@ -40,23 +41,17 @@ export class ActivePullRequestTool implements vscode.LanguageModelTool line.startsWith('data:')) - .forEach(line => { - try { - const obj = JSON.parse(line.replace(/^data:\s*/, '')); - if (Array.isArray(obj.choices)) { - for (const choice of obj.choices) { - const delta = choice.delta || {}; - if (typeof delta.content === 'string' && !!delta.role) { - result.push(delta.content); - } - } - } - } catch { /* ignore parse errors */ } - }); - + const logChunks = parseSessionLogs(logsResponseText); + + for (const chunk of logChunks) { + for (const choice of chunk.choices) { + const delta = choice.delta || {}; + if (typeof delta.content === 'string' && !!delta.role) { + result.push(delta.content); + } + } + } + return result; } diff --git a/src/view/sessionLogView.ts b/src/view/sessionLogView.ts index 521190db47..bbd3fce38f 100644 --- a/src/view/sessionLogView.ts +++ b/src/view/sessionLogView.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { parseSessionLogs } from '../../common/sessionParsing'; import type * as messages from '../../webviews/sessionLogView/messages'; import { Disposable, disposeAll } from '../common/lifecycle'; import { ITelemetry } from '../common/telemetry'; @@ -413,7 +414,7 @@ class SessionLogView extends Disposable { type: 'loaded', pullInfo: this._source.type === 'pull' ? this._source.pullRequest : undefined, info: apiResponse.info, - logs: apiResponse.logs, + logs: parseSessionLogs(apiResponse.logs), setupSteps: apiResponse.setupSteps } as messages.LoadedMessage); @@ -435,7 +436,7 @@ class SessionLogView extends Disposable { type: 'update', pullInfo: this._source.type === 'pull' ? this._source.pullRequest : undefined, info: apiResult.info, - logs: apiResult.logs, + logs: parseSessionLogs(apiResult.logs), setupSteps: apiResult.setupSteps } as messages.UpdateMessage); diff --git a/webviews/sessionLogView/app.tsx b/webviews/sessionLogView/app.tsx index 0c9de2b6d2..3444a1d3a2 100644 --- a/webviews/sessionLogView/app.tsx +++ b/webviews/sessionLogView/app.tsx @@ -7,7 +7,7 @@ import { shikiToMonaco } from '@shikijs/monaco'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import * as React from 'react'; import { createHighlighter } from 'shiki'; -import { parseSessionLogs, SessionResponseLogChunk } from '../../common/sessionParsing'; +import { SessionResponseLogChunk } from '../../common/sessionParsing'; import { vscode } from '../common/message'; import type * as messages from './messages'; import { SessionInfo, SessionSetupStepResponse } from './sessionsApi'; @@ -49,7 +49,7 @@ export function App() { setState({ state: 'ready', info: message.info, - logs: parseSessionLogs(message.logs), + logs: message.logs, pullInfo: message.pullInfo, setupSteps: message.setupSteps }); diff --git a/webviews/sessionLogView/messages.ts b/webviews/sessionLogView/messages.ts index 9905d93177..81d43e50e6 100644 --- a/webviews/sessionLogView/messages.ts +++ b/webviews/sessionLogView/messages.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { SessionResponseLogChunk } from '../../common/sessionParsing'; import { SessionLinkInfo } from '../../src/common/timelineEvent'; import { SessionInfo, SessionSetupStepResponse } from './sessionsApi'; @@ -19,7 +20,7 @@ export interface LoadedMessage { type: 'loaded'; pullInfo: PullInfo | undefined; info: SessionInfo; - logs: string; + logs: readonly SessionResponseLogChunk[]; setupSteps: SessionSetupStepResponse[] | undefined; } @@ -27,7 +28,7 @@ export interface UpdateMessage { type: 'update'; pullInfo: PullInfo | undefined; info: SessionInfo; - logs: string; + logs: readonly SessionResponseLogChunk[]; setupSteps: SessionSetupStepResponse[] | undefined; }