import { IGitExtensionService } from '../../../platform/git/common/gitExtensionService'; import { DebugRecorderBookmark } from '../../../platform/inlineEdits/common/debugRecorderBookmark'; import { IObservableDocument, ObservableWorkspace } from '../../../platform/inlineEdits/common/observableWorkspace'; import { IStatelessNextEditModelTelemetry, IStatelessNextEditTelemetry, StatelessNextEditRequest } from '../../../platform/inlineEdits/common/statelessNextEditProvider'; import { INotebookService } from '../../../platform/notebook/common/notebookService'; import { ITelemetryService } from '../../../platform/telemetry/common/telemetry'; import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService'; import { LogEntry } from '../../../platform/workspaceRecorder/common/workspaceLog'; import { Disposable, IDisposable } from '../../../util/vs/base/common/lifecycle'; import { StringReplacement } from '../../../util/vs/editor/common/core/edits/stringEdit'; import { DebugRecorder } from './debugRecorder'; import { INesConfigs } from './nesConfigs'; import { INextEditDisplayLocation, INextEditResult } from './nextEditResult'; /** * GitHub telemetry event name for NES (Next Edit Suggestion) telemetry. * * Used for both the standard event ({@link ITelemetryService.sendGHTelemetryEvent}) emitted per * suggestion, and the enhanced events ({@link ITelemetryService.sendEnhancedGHTelemetryEvent}) * emitted both per-suggestion (here) and periodically (see `ContinuousEnhancedTelemetrySender`). * Enhanced continuous events are disambiguated by a `continuous: 'true'` property. */ export declare const NES_GH_TELEMETRY_EVENT_NAME = "copilot-nes/provideInlineEdit"; export type NextEditTelemetryStatus = 'new' | 'requested' | `noEdit:${string}` | 'docChanged' | 'emptyEdits' | 'emptyEditsButHasNextCursorPosition' | 'previouslyRejected' | 'previouslyRejectedCache' | 'accepted' | 'notAccepted' | 'rejected'; export type NesAcceptance = 'accepted' | 'notAccepted' | 'rejected'; export type EnhancedTelemetrySendingReasonKind = 'idle' | 'hard_cap' | 'user_jump'; export interface IEnhancedTelemetrySendingReason { readonly reason: EnhancedTelemetrySendingReasonKind; readonly details: { readonly idleTimeoutMs?: number; readonly hardCapTimeoutMs?: number; readonly from?: { readonly file: string; readonly line: number; }; readonly to?: { readonly file: string; readonly line: number | undefined; }; }; } export interface IAlternativeAction { readonly text: string | undefined; readonly textLength: number; readonly selection: ITelemetryRange[]; readonly edits: ITelemetryEdit[]; readonly tags: string[]; readonly recording: ITelemetryRecording | undefined; } export interface ITelemetryEdit { readonly time: string; readonly start: number; readonly endExclusive: number; readonly newText: string; } export interface ITelemetryRange { readonly start: number; readonly endExclusive: number; } export interface ITelemetryRecording { readonly entries: LogEntry[] | undefined; readonly entriesSize: number; readonly requestTime: number; } export declare const enum ReusedRequestKind { Speculative = "speculative", Async = "async" } export interface ILlmNESTelemetry extends Partial { readonly providerId: string; readonly headerRequestId: string | undefined; readonly nextEditProviderDuration: number | undefined; readonly fetchStartedAfterMs: number | undefined; readonly isFromCache: boolean; readonly reusedRequest: ReusedRequestKind | undefined; readonly subsequentEditOrder: number | undefined; readonly sourcePatchIndex: number | undefined; readonly activeDocumentOriginalLineCount: number | undefined; readonly activeDocumentEditsCount: number | undefined; readonly activeDocumentLanguageId: string | undefined; readonly activeDocumentRepository: string | undefined; readonly hasNextEdit: boolean; readonly wasPreviouslyRejected: boolean; readonly status: NextEditTelemetryStatus; readonly nextEditProviderError: string | undefined; readonly nesConfigs: INesConfigs | undefined; readonly repositoryUrls: string[] | undefined; readonly documentsCount: number | undefined; readonly editsCount: number | undefined; readonly isNotebook: boolean; readonly notebookType: string | undefined; readonly alternativeAction: IAlternativeAction | undefined; } export interface IDiagnosticsTelemetry { readonly diagnosticType: string | undefined; readonly diagnosticDroppedReasons: string | undefined; readonly diagnosticDistanceToUnknownDiagnostic: number | undefined; readonly diagnosticDistanceToAlternativeDiagnostic: number | undefined; readonly diagnosticHasAlternativeDiagnosticForSameRange: boolean | undefined; readonly diagnosticHasExistingSameFileImport: boolean | undefined; readonly diagnosticIsLocalImport: boolean | undefined; readonly diagnosticAlternativeImportsCount: number | undefined; } export interface INextEditProviderTelemetry extends ILlmNESTelemetry, IDiagnosticsTelemetry { readonly opportunityId: string; readonly requestN: number; readonly isShown: boolean; readonly acceptance: NesAcceptance; readonly disposalReason: string | undefined; readonly supersededByOpportunityId: string | undefined; readonly status: NextEditTelemetryStatus; readonly nextEditProviderError: string | undefined; readonly activeDocumentRepository: string | undefined; readonly repositoryUrls: string[] | undefined; readonly alternativeAction: IAlternativeAction | undefined; readonly postProcessingOutcome: string | undefined; readonly isNESForAnotherDoc: boolean; readonly notebookCellMarkerCount: number; readonly notebookCellMarkerIndex: number; readonly notebookId: string | undefined; readonly notebookCellLines: string | undefined; readonly isActiveDocument?: boolean; readonly isMultilineEdit?: boolean; /** * Signed line distance from the cursor line to the suggested edit's range start line * (`rangeStartLine - cursorLine`, 0-based). Undefined for cross-document suggestions. */ readonly suggestionLineDistanceToCursor?: number; readonly isEolDifferent?: boolean; readonly isNextEditorVisible?: boolean; readonly isNextEditorRangeVisible?: boolean; readonly isNaturalLanguageDominated: boolean; readonly hadLlmNES: boolean; readonly hadDiagnosticsNES: boolean; readonly pickedNES: 'llm' | 'diagnostics' | undefined; readonly configIsDiagnosticsNESEnabled: boolean; readonly userTypingDisagreed: boolean | undefined; } export declare class LlmNESTelemetryBuilder extends Disposable { private readonly _gitExtensionService; private readonly _notebookService; private readonly _workspaceService; private readonly _providerId; private readonly _doc; private readonly _debugRecorder?; private _requestBookmark?; build(includeAlternativeAction: boolean): ILlmNESTelemetry; private _startTime; /** Dependent on the observable document to track edits and selections */ private editCollectingInfo; get originalSelectionLine(): number | undefined; /** Refresh the request bookmark so the telemetry `requestTime` lines up with the * moment the document/selection were snapshotted for prompt construction. */ setRequestBookmark(bookmark: DebugRecorderBookmark): void; /** * @param _doc passing an observable document allows to track edits and selections */ constructor(_gitExtensionService: IGitExtensionService | undefined, _notebookService: INotebookService | undefined, _workspaceService: IWorkspaceService | undefined, _providerId: string, _doc: IObservableDocument | undefined, _debugRecorder?: DebugRecorder | undefined, _requestBookmark?: DebugRecorderBookmark | undefined); private _nesConfigs; setNESConfigs(nesConfigs: INesConfigs): this; private _headerRequestId; setHeaderRequestId(uuid: string): this; private _isFromCache; setIsFromCache(): this; private _cachedModelTelemetry; setCachedModelTelemetry(modelTelemetry: IStatelessNextEditModelTelemetry): this; private _reusedRequest; setReusedRequest(kind: ReusedRequestKind): this; private _subsequentEditOrder; setSubsequentEditOrder(subsequentEditOrder: number | undefined): this; private _sourcePatchIndex; setSourcePatchIndex(sourcePatchIndex: number | undefined): this; private _request; setRequest(request: StatelessNextEditRequest): this; private _statelessNextEditTelemetry; setStatelessNextEditTelemetry(statelessNextEditTelemetry: IStatelessNextEditTelemetry): this; private _hasNextEdit; setHasNextEdit(hasNextEdit: boolean): this; private _wasPreviouslyRejected; setWasPreviouslyRejected(): this; private _duration; markEndTime(): this; private _status; setStatus(status: NextEditTelemetryStatus): this; private _nextEditProviderError; setNextEditProviderError(nextEditProviderError: string | undefined): this; } interface IDiagnosticTelemetryRun { alternativeImportsCount?: number; hasExistingSameFileImport?: boolean; isLocalImport?: boolean; distanceToUnknownDiagnostic?: number; distanceToAlternativeDiagnostic?: number; hasAlternativeDiagnosticForSameRange?: boolean; } export declare class DiagnosticsTelemetryBuilder { build(): IDiagnosticsTelemetry; populate(telemetry: DiagnosticsTelemetryBuilder): void; private _type; setType(type: string): this; private _droppedReasons; addDroppedReason(reason: string): this; private _diagnosticRunTelemetry; setDiagnosticRunTelemetry(diagnosticRun: IDiagnosticTelemetryRun): this; } export declare class NextEditProviderTelemetryBuilder extends Disposable { readonly doc: IObservableDocument | undefined; private static providerIdToReqN; /** * Whether telemetry for this builder has been sent -- only for ordinary telemetry, not enhanced telemetry */ private _isSent; get isSent(): boolean; markAsSent(): void; build(includeAlternativeAction: boolean): INextEditProviderTelemetry; private _requestN; private readonly _nesBuilder; get nesBuilder(): LlmNESTelemetryBuilder; private readonly _diagnosticsBuilder; get diagnosticsBuilder(): DiagnosticsTelemetryBuilder; /** * @param _doc passing an observable document allows to track edits and selections */ constructor(gitExtensionService: IGitExtensionService | undefined, notebookService: INotebookService | undefined, workspaceService: IWorkspaceService | undefined, providerId: string, doc: IObservableDocument | undefined, debugRecorder?: DebugRecorder, requestBookmark?: DebugRecorderBookmark); private _opportunityId; setOpportunityId(uuid: string): this; private _isShown; setAsShown(): this; private _acceptance; setAcceptance(acceptance: NesAcceptance): this; private _disposalReason; setDisposalReason(disposalReason: string | undefined): this; private _supersededByOpportunityId; setSupersededBy(opportunityId: string | undefined): this; private _userTypingDisagreed; setUserTypingDisagreed(userTypingDisagreed: boolean): this; private _nesTypePicked; setPickedNESType(nesTypePicked: 'llm' | 'diagnostics'): this; private _isActiveDocument?; setIsActiveDocument(isActive: boolean): this; private _notebookCellMarkerCount; setNotebookCellMarkerCount(count: number): this; private _isMultilineEdit?; setIsMultilineEdit(isMultiLine: boolean): this; private _suggestionLineDistanceToCursor?; /** * @param distance Signed line distance from the cursor line to the suggested edit's range * start line (`rangeStartLine - cursorLine`, 0-based). Pass `undefined` for cross-document * suggestions where the distance is not meaningful. */ setSuggestionLineDistanceToCursor(distance: number | undefined): this; private _isEolDifferent?; setIsEolDifferent(isEolDifferent: boolean): this; private _isNextEditorVisible?; setIsNextEditorVisible(isVisible: boolean): this; private _isNextEditorRangeVisible?; setIsNextEditorRangeVisible(isVisible: boolean): this; private _notebookId?; setNotebookId(notebookId: string): this; private _notebookCellLines?; setNotebookCellLines(notebookCellLines: string): this; private _notebookCellMarkerIndex; setNotebookCellMarkerIndex(index: number): this; private _isNESForAnotherDoc; setIsNESForOtherEditor(isForAnotherDoc: boolean): this; private _hadLlmNES; setHadLlmNES(boolean: boolean): this; private _hadDiagnosticsNES; setHadDiagnosticsNES(boolean: boolean): this; setStatus(status: NextEditTelemetryStatus): this; private _configIsDiagnosticsNESEnabled; setConfigIsDiagnosticsNESEnabled(boolean: boolean): this; private _isNaturalLanguageDominated; setIsNaturalLanguageDominated(isNaturalLanguageDominated: boolean): this; private _postProcessingOutcome; setPostProcessingOutcome(suggestion: { edit: StringReplacement; isInlineCompletion: boolean; displayLocation?: INextEditDisplayLocation; }): this; } export declare class TelemetrySender implements IDisposable { private readonly _workspace; private readonly _telemetryService; private readonly _map; private _idleDetector; constructor(_workspace: ObservableWorkspace | undefined, _telemetryService: ITelemetryService); /** * Schedule sending enhanced telemetry for a NES suggestion. * * After a 2-minute initial delay, enters an idle-detection phase that monitors all workspace documents * and finally sends the telemetry event when one of these conditions is met: * * - **idle** (5s): No document edits across the entire workspace for 5 seconds. * - **user_jump**: User moves cursor to a different line or different file (detected via * {@link IObservableDocument.primarySelectionLine} snapshot diffs. * - **hard_cap** (30s): Forced send after 30 seconds regardless of activity. * * Note: only documents tracked by the {@link ObservableWorkspace} are monitored. Documents in * languages where Copilot completions are disabled (e.g. markdown) and copilot-ignored files are excluded, * so activity in those files won't reset the idle timer. This matches the scope of {@link DebugRecorder}. */ scheduleSendingEnhancedTelemetry(nextEditResult: INextEditResult, builder: NextEditProviderTelemetryBuilder): void; private _enterIdleDetection; private _releaseIdleDetector; /** Send all entries that are in the idle-detection phase (have no initial timeout pending) with a shared reason. */ private _sendAllPendingInIdlePhase; /** Send all entries in idle-detection phase with user_jump, using per-entry `from` positions. */ private _sendAllPendingInIdlePhaseWithJump; /** Send enhanced telemetry for a single entry that's in the idle-detection phase. */ private _sendForEntry; private _removeEntry; private _buildAndSendEnhancedTelemetry; /** * Send telemetry for the next edit result in case it has already been rejected or contains no edits to be shown. */ sendTelemetry(nextEditResult: INextEditResult | undefined, builder: NextEditProviderTelemetryBuilder): void; sendTelemetryForBuilder(builder: NextEditProviderTelemetryBuilder): void; private _doSendTelemetry; private _sendTelemetryToBoth; private _doSendEnhancedTelemetry; /** * If `value` is undefined, return undefined, otherwise return 1 if `value` is true, 0 otherwise. */ private _boolToNum; dispose(): void; } export {}; //# sourceMappingURL=nextEditProviderTelemetry.d.ts.map