Path: blob/main/extensions/copilot/src/platform/chat/common/sessionTranscriptService.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { createServiceIdentifier } from '../../../util/common/services';6import { URI } from '../../../util/vs/base/common/uri';78export const ISessionTranscriptService = createServiceIdentifier<ISessionTranscriptService>('ISessionTranscriptService');910// #region Transcript Entry Types1112/**13* Common fields shared by all transcript entries.14*/15interface TranscriptEntryBase {16/** Entry type discriminator. */17readonly type: string;18/** Entry-specific data payload. */19readonly data: unknown;20/** Unique entry identifier. */21readonly id: string;22/** ISO 8601 timestamp of when this entry was created. */23readonly timestamp: string;24/** ID of the previous entry for ordering (null for first entry). */25readonly parentId: string | null;26}2728export interface SessionStartData {29readonly sessionId: string;30readonly version: number;31readonly producer: string;32readonly copilotVersion: string;33readonly vscodeVersion: string;34readonly startTime: string;35readonly context?: {36readonly cwd?: string;37};38}3940export interface SessionStartEntry extends TranscriptEntryBase {41readonly type: 'session.start';42readonly data: SessionStartData;43}4445export interface UserMessageData {46readonly content: string;47readonly attachments?: readonly unknown[];48}4950export interface UserMessageEntry extends TranscriptEntryBase {51readonly type: 'user.message';52readonly data: UserMessageData;53}5455export interface AssistantTurnStartData {56readonly turnId: string;57}5859export interface AssistantTurnStartEntry extends TranscriptEntryBase {60readonly type: 'assistant.turn_start';61readonly data: AssistantTurnStartData;62}6364export interface ToolRequest {65readonly toolCallId: string;66readonly name: string;67readonly arguments: string;68readonly type: 'function';69}7071export interface AssistantMessageData {72readonly messageId: string;73readonly content: string;74readonly toolRequests: readonly ToolRequest[];75readonly reasoningText?: string;76}7778export interface AssistantMessageEntry extends TranscriptEntryBase {79readonly type: 'assistant.message';80readonly data: AssistantMessageData;81}8283export interface ToolExecutionStartData {84readonly toolCallId: string;85readonly toolName: string;86readonly arguments: unknown;87}8889export interface ToolExecutionStartEntry extends TranscriptEntryBase {90readonly type: 'tool.execution_start';91readonly data: ToolExecutionStartData;92}9394export interface ToolExecutionCompleteData {95readonly toolCallId: string;96readonly success: boolean;97readonly result?: {98readonly content: string;99};100}101102export interface ToolExecutionCompleteEntry extends TranscriptEntryBase {103readonly type: 'tool.execution_complete';104readonly data: ToolExecutionCompleteData;105}106107export interface AssistantTurnEndData {108readonly turnId: string;109}110111export interface AssistantTurnEndEntry extends TranscriptEntryBase {112readonly type: 'assistant.turn_end';113readonly data: AssistantTurnEndData;114}115116export type TranscriptEntry =117| SessionStartEntry118| UserMessageEntry119| AssistantTurnStartEntry120| AssistantMessageEntry121| ToolExecutionStartEntry122| ToolExecutionCompleteEntry123| AssistantTurnEndEntry;124125// #endregion126127// #region Historical Replay Types128129/**130* A tool call from a historical round, used when replaying conversation131* history into a transcript file.132*/133export interface IHistoricalToolCall {134readonly name: string;135readonly arguments: string;136readonly id: string;137}138139/**140* A single assistant round from conversation history.141* Maps to a `user.message → assistant.turn_start → assistant.message → assistant.turn_end`142* sequence in the transcript.143*/144export interface IHistoricalToolCallRound {145/** The assistant's text response for this round. */146readonly response: string;147/** Tool calls made by the assistant in this round. */148readonly toolCalls: readonly IHistoricalToolCall[];149/** Optional reasoning / thinking text. */150readonly reasoningText?: string;151/** Epoch millis (`Date.now()`) when this round started, if known. */152readonly timestamp?: number;153}154155/**156* A single turn from conversation history, containing the user message157* and all assistant rounds that followed.158*/159export interface IHistoricalTurn {160/** The user's prompt text for this turn. */161readonly userMessage: string;162/** Epoch millis (`Date.now()`) when this turn started. */163readonly timestamp: number;164/** The assistant rounds that occurred during this turn. */165readonly rounds: readonly IHistoricalToolCallRound[];166}167168// #endregion169170export interface ISessionTranscriptService {171readonly _serviceBrand: undefined;172173/**174* Start tracking a new session. Creates the transcript file.175*176* If `history` is provided and no transcript file exists on disk yet,177* the historical turns are replayed into the transcript before the178* current turn begins. If a file already exists, history is ignored.179*180* @param sessionId Unique session identifier (typically `conversation.sessionId`).181* @param context Optional context about the session environment.182* @param history Previous turns to replay if the transcript must be created from scratch.183*/184startSession(sessionId: string, context?: { cwd?: string }, history?: readonly IHistoricalTurn[]): Promise<void>;185186/**187* Record the user's prompt message.188* Entries are buffered; call {@link flush} to write to disk.189*/190logUserMessage(sessionId: string, content: string, attachments?: readonly unknown[]): void;191192/**193* Record the start of an assistant turn (one iteration of the tool calling loop).194* Entries are buffered; call {@link flush} to write to disk.195*/196logAssistantTurnStart(sessionId: string, turnId: string): void;197198/**199* Record an assistant message containing text and/or tool call requests.200* Entries are buffered; call {@link flush} to write to disk.201*/202logAssistantMessage(sessionId: string, content: string, toolRequests: readonly ToolRequest[], reasoningText?: string): void;203204/**205* Record the start of a tool execution.206* Entries are buffered; call {@link flush} to write to disk.207*/208logToolExecutionStart(sessionId: string, toolCallId: string, toolName: string, args: unknown): void;209210/**211* Record the completion of a tool execution.212* Entries are buffered; call {@link flush} to write to disk.213*/214logToolExecutionComplete(sessionId: string, toolCallId: string, success: boolean, resultContent?: string): void;215216/**217* Record the end of an assistant turn.218* Entries are buffered; call {@link flush} to write to disk.219*/220logAssistantTurnEnd(sessionId: string, turnId: string): void;221222/**223* Flush all buffered transcript entries for a session to disk.224* Safe to call multiple times; concurrent flushes are serialized.225*/226flush(sessionId: string): Promise<void>;227228/**229* Mark a session as ended. The transcript file is retained for lazy cleanup.230*/231endSession(sessionId: string): Promise<void>;232233/**234* Get the URI of the transcript file for a session, if one exists.235* Returns `undefined` if the session has no transcript.236*/237getTranscriptPath(sessionId: string): URI | undefined;238239/**240* Get the current number of lines in the transcript for a session.241* Returns `undefined` if the session is not active.242*/243getLineCount(sessionId: string): number | undefined;244245/**246* Remove transcript files for sessions that are no longer active,247* keeping at most `maxRetained` most-recent ended sessions.248*/249cleanupOldTranscripts(maxRetained?: number): Promise<void>;250251/**252* Check whether a URI is under the transcripts storage directory.253* Used by {@link assertFileOkForTool} to allowlist tool reads.254*/255isTranscriptUri(uri: URI): boolean;256}257258export class NullSessionTranscriptService implements ISessionTranscriptService {259declare readonly _serviceBrand: undefined;260261async startSession(): Promise<void> { }262logUserMessage(): void { }263logAssistantTurnStart(): void { }264logAssistantMessage(): void { }265logToolExecutionStart(): void { }266logToolExecutionComplete(): void { }267logAssistantTurnEnd(): void { }268async flush(): Promise<void> { }269async endSession(): Promise<void> { }270getTranscriptPath(): URI | undefined { return undefined; }271getLineCount(): number | undefined { return undefined; }272async cleanupOldTranscripts(): Promise<void> { }273isTranscriptUri(): boolean { return false; }274}275276277