"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var XtabProvider_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.XtabProvider = void 0;
exports.filterOutEditsWithSubstrings = filterOutEditsWithSubstrings;
exports.computeAreaAroundEditWindowLinesRange = computeAreaAroundEditWindowLinesRange;
exports.mapChatFetcherErrorToNoNextEditReason = mapChatFetcherErrorToNoNextEditReason;
exports.overrideModelConfig = overrideModelConfig;
exports.pickSystemPrompt = pickSystemPrompt;
exports.determineLanguageContextOptions = determineLanguageContextOptions;
exports.getPredictionContents = getPredictionContents;
const chatMLFetcher_1 = require("../../../platform/chat/common/chatMLFetcher");
const commonTypes_1 = require("../../../platform/chat/common/commonTypes");
const configurationService_1 = require("../../../platform/configuration/common/configurationService");
const diffService_1 = require("../../../platform/diff/common/diffService");
const proxyXtabEndpoint_1 = require("../../../platform/endpoint/node/proxyXtabEndpoint");
const ignoreService_1 = require("../../../platform/ignore/common/ignoreService");
const documentId_1 = require("../../../platform/inlineEdits/common/dataTypes/documentId");
const edit_1 = require("../../../platform/inlineEdits/common/dataTypes/edit");
const languageId_1 = require("../../../platform/inlineEdits/common/dataTypes/languageId");
const nextCursorLinePrediction_1 = require("../../../platform/inlineEdits/common/dataTypes/nextCursorLinePrediction");
const xtabPromptOptions = __importStar(require("../../../platform/inlineEdits/common/dataTypes/xtabPromptOptions"));
const xtabPromptOptions_1 = require("../../../platform/inlineEdits/common/dataTypes/xtabPromptOptions");
const inlineEditsModelService_1 = require("../../../platform/inlineEdits/common/inlineEditsModelService");
const responseProcessor_1 = require("../../../platform/inlineEdits/common/responseProcessor");
const statelessNextEditProvider_1 = require("../../../platform/inlineEdits/common/statelessNextEditProvider");
const statelessNextEditProviders_1 = require("../../../platform/inlineEdits/common/statelessNextEditProviders");
const languageContextProviderService_1 = require("../../../platform/languageContextProvider/common/languageContextProviderService");
const languageDiagnosticsService_1 = require("../../../platform/languages/common/languageDiagnosticsService");
const languageContextService_1 = require("../../../platform/languageServer/common/languageContextService");
const simulationTestContext_1 = require("../../../platform/simulationTestContext/common/simulationTestContext");
const nullExperimentationService_1 = require("../../../platform/telemetry/common/nullExperimentationService");
const workspaceService_1 = require("../../../platform/workspace/common/workspaceService");
const async_1 = require("../../../util/common/async");
const asyncIterableUtils_1 = require("../../../util/common/asyncIterableUtils");
const backwardCompatSetting_1 = require("../../../util/common/backwardCompatSetting");
const errors_1 = require("../../../util/common/errors");
const result_1 = require("../../../util/common/result");
const assert_1 = require("../../../util/vs/base/common/assert");
const async_2 = require("../../../util/vs/base/common/async");
const cancellation_1 = require("../../../util/vs/base/common/cancellation");
const path_1 = require("../../../util/vs/base/common/path");
const stopwatch_1 = require("../../../util/vs/base/common/stopwatch");
const uri_1 = require("../../../util/vs/base/common/uri");
const lineEdit_1 = require("../../../util/vs/editor/common/core/edits/lineEdit");
const stringEdit_1 = require("../../../util/vs/editor/common/core/edits/stringEdit");
const position_1 = require("../../../util/vs/editor/common/core/position");
const range_1 = require("../../../util/vs/editor/common/core/range");
const lineRange_1 = require("../../../util/vs/editor/common/core/ranges/lineRange");
const offsetRange_1 = require("../../../util/vs/editor/common/core/ranges/offsetRange");
const abstractText_1 = require("../../../util/vs/editor/common/core/text/abstractText");
const positionToOffset_1 = require("../../../util/vs/editor/common/core/text/positionToOffset");
const instantiation_1 = require("../../../util/vs/platform/instantiation/common/instantiation");
const vscodeTypesShim_1 = require("../../../util/common/test/shims/vscodeTypesShim");
const nearbyCursorInlineEditProvider_1 = require("../../inlineEdits/common/nearbyCursorInlineEditProvider");
const userInteractionMonitor_1 = require("../../inlineEdits/common/userInteractionMonitor");
const importFiltering_1 = require("../../inlineEdits/node/importFiltering");
const fetchStreamError_1 = require("../common/fetchStreamError");
const inlineSuggestion_1 = require("../common/inlineSuggestion");
const lintErrors_1 = require("../common/lintErrors");
const promptCrafting_1 = require("../common/promptCrafting");
const promptCraftingUtils_1 = require("../common/promptCraftingUtils");
const similarFilesContextService_1 = require("../common/similarFilesContextService");
const systemMessages_1 = require("../common/systemMessages");
const tags_1 = require("../common/tags");
const terminalOutput_1 = require("../common/terminalOutput");
const xtabCurrentDocument_1 = require("../common/xtabCurrentDocument");
const cursorLineDivergence_1 = require("./cursorLineDivergence");
const editIntent_1 = require("./editIntent");
const responseFormatHandlers_1 = require("./responseFormatHandlers");
const xtabCustomDiffPatchResponseHandler_1 = require("./xtabCustomDiffPatchResponseHandler");
const xtabEndpoint_1 = require("./xtabEndpoint");
const xtabNextCursorPredictor_1 = require("./xtabNextCursorPredictor");
const xtabUtils_1 = require("./xtabUtils");
/**
 * Returns true if the user has made document edits since the request was created.
 * Used to skip costly sub-requests (e.g. next cursor prediction) whose results will
 * be stale by the time they return.
 */
function hasUserTypedSinceRequestStarted(request) {
    return request.intermediateUserEdit === undefined || !request.intermediateUserEdit.isEmpty();
}
var RetryState;
(function (RetryState) {
    class NotRetrying {
        static { this.INSTANCE = new NotRetrying(); }
    }
    RetryState.NotRetrying = NotRetrying;
    class Retrying {
        constructor(reason) {
            this.reason = reason;
        }
    }
    RetryState.Retrying = Retrying;
})(RetryState || (RetryState = {}));
var FetchResult;
(function (FetchResult) {
    class Lines {
        constructor(linesStream, getResponseSoFar, fetchRequestStopWatch) {
            this.linesStream = linesStream;
            this.getResponseSoFar = getResponseSoFar;
            this.fetchRequestStopWatch = fetchRequestStopWatch;
        }
    }
    FetchResult.Lines = Lines;
    class ModelNotFound {
        static { this.INSTANCE = new ModelNotFound(); }
    }
    FetchResult.ModelNotFound = ModelNotFound;
    class FetchFailure {
        constructor(reason) {
            this.reason = reason;
        }
    }
    FetchResult.FetchFailure = FetchFailure;
})(FetchResult || (FetchResult = {}));
let XtabProvider = class XtabProvider {
    static { XtabProvider_1 = this; }
    static { this.ID = configurationService_1.XTabProviderId; }
    static { this.computeTokens = (s) => Math.floor(s.length / 4); }
    constructor(modelService, simulationCtx, instaService, workspaceService, diffService, configService, expService, langCtxService, langDiagService, ignoreService, similarFilesContextService) {
        this.modelService = modelService;
        this.simulationCtx = simulationCtx;
        this.instaService = instaService;
        this.workspaceService = workspaceService;
        this.diffService = diffService;
        this.configService = configService;
        this.expService = expService;
        this.langCtxService = langCtxService;
        this.langDiagService = langDiagService;
        this.ignoreService = ignoreService;
        this.similarFilesContextService = similarFilesContextService;
        this.ID = XtabProvider_1.ID;
        this.forceUseDefaultModel = false;
        this.userInteractionMonitor = this.instaService.createInstance(userInteractionMonitor_1.UserInteractionMonitor);
        this.terminalMonitor = this.instaService.createInstance(terminalOutput_1.TerminalMonitor);
        this.nextCursorPredictor = this.instaService.createInstance(xtabNextCursorPredictor_1.XtabNextCursorPredictor, XtabProvider_1.computeTokens);
    }
    handleAcceptance() {
        this.userInteractionMonitor.handleAcceptance();
    }
    handleRejection() {
        this.userInteractionMonitor.handleRejection();
    }
    handleIgnored() {
        this.userInteractionMonitor.handleIgnored();
    }
    async *provideNextEdit(request, logger, logContext, cancellationToken) {
        const telemetry = new statelessNextEditProvider_1.StatelessNextEditTelemetryBuilder(request.headerRequestId);
        logContext.setProviderStartTime();
        try {
            if (request.xtabEditHistory.length === 0) {
                const noSuggestionReason = new statelessNextEditProvider_1.NoNextEditReason.ActiveDocumentHasNoEdits();
                return new statelessNextEditProvider_1.WithStatelessProviderTelemetry(noSuggestionReason, telemetry.build(result_1.Result.error(noSuggestionReason)));
            }
            const delaySession = this.userInteractionMonitor.createDelaySession(request.providerRequestStartDateTime);
            const tracing = { tracer: logger, logContext, telemetry };
            const iterator = this.doGetNextEdit(request, delaySession, tracing, cancellationToken, RetryState.NotRetrying.INSTANCE);
            let res = await iterator.next(); // for-async-await loop doesn't work because we need to access the final return value
            while (!res.done) {
                yield new statelessNextEditProvider_1.WithStatelessProviderTelemetry(res.value, telemetry.build(result_1.Result.ok(undefined)));
                res = await iterator.next();
            }
            const noNextEditReason = res.value;
            if (noNextEditReason instanceof statelessNextEditProvider_1.NoNextEditReason.GotCancelled) {
                logContext.setIsSkipped();
            }
            return new statelessNextEditProvider_1.WithStatelessProviderTelemetry(noNextEditReason, telemetry.build(result_1.Result.error(noNextEditReason)));
        }
        catch (err) {
            const error = errors_1.ErrorUtils.fromUnknown(err);
            const noSuggestionReason = new statelessNextEditProvider_1.NoNextEditReason.Unexpected(error);
            return new statelessNextEditProvider_1.WithStatelessProviderTelemetry(noSuggestionReason, telemetry.build(result_1.Result.error(noSuggestionReason)));
        }
        finally {
            logContext.setProviderEndTime();
        }
    }
    doGetNextEdit(request, delaySession, tracing, cancellationToken, retryState) {
        return this.doGetNextEditWithSelection(request, (0, nearbyCursorInlineEditProvider_1.getOrDeduceSelectionFromLastEdit)(request.getActiveDocument()), delaySession, tracing, cancellationToken, retryState);
    }
    async *doGetNextEditWithSelection(request, selection, delaySession, tracing, cancellationToken, retryState, 
    /**
     * For cursor jump scenarios, this is the edit window around the original cursor position
     * (before the jump). When provided, yielded edits will include this as `originalWindow`
     * so the cache can serve the edit when the cursor is in either location.
     */
    originalEditWindow) {
        const tracer = tracing.tracer.createSubLogger(['XtabProvider', 'doGetNextEditWithSelection']);
        const { logContext, telemetry } = tracing;
        const activeDocument = request.getActiveDocument();
        if (selection === null) {
            return new statelessNextEditProvider_1.NoNextEditReason.Uncategorized(new Error('NoSelection'));
        }
        const { promptOptions, modelServiceConfig } = this.determineModelConfiguration(activeDocument);
        telemetry.setModelConfig(JSON.stringify(modelServiceConfig));
        const endpoint = this.getEndpointWithLogging(promptOptions.modelName, logContext, telemetry);
        const cursorPosition = new position_1.Position(selection.endLineNumber, selection.endColumn);
        const currentDocument = new xtabCurrentDocument_1.CurrentDocument(activeDocument.documentAfterEdits, cursorPosition);
        this._configureDebounceTimings(request, currentDocument, promptOptions, telemetry, delaySession, tracer);
        const areaAroundEditWindowLinesRange = computeAreaAroundEditWindowLinesRange(currentDocument);
        const editWindowLinesRange = this.computeEditWindowLinesRange(currentDocument, request, tracer, telemetry);
        const cursorOriginalLinesOffset = Math.max(0, currentDocument.cursorLineOffset - editWindowLinesRange.start);
        const editWindowLastLineLength = currentDocument.transformer.getLineLength(editWindowLinesRange.endExclusive);
        const editWindow = currentDocument.transformer.getOffsetRange(new range_1.Range(editWindowLinesRange.start + 1, 1, editWindowLinesRange.endExclusive, editWindowLastLineLength + 1));
        request.requestEditWindow = originalEditWindow
            ? new statelessNextEditProvider_1.RequestEditWindowWithCursorJump(editWindow, originalEditWindow)
            : new statelessNextEditProvider_1.RequestEditWindow(editWindow);
        const editWindowLines = currentDocument.lines.slice(editWindowLinesRange.start, editWindowLinesRange.endExclusive);
        const editWindowTokenLimit = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabEditWindowMaxTokens, this.expService);
        if (editWindowTokenLimit !== undefined && (0, promptCraftingUtils_1.countTokensForLines)(editWindowLines, XtabProvider_1.computeTokens) > editWindowTokenLimit) {
            return new statelessNextEditProvider_1.NoNextEditReason.PromptTooLarge('editWindow');
        }
        // Expected: editWindow.substring(activeDocument.documentAfterEdits.value) === editWindowLines.join('\n')
        const doesIncludeCursorTag = editWindowLines.some(line => line.includes(tags_1.PromptTags.CURSOR));
        const shouldRemoveCursorTagFromResponse = !doesIncludeCursorTag; // we'd like to remove the tag only if the original edit-window didn't include the tag
        const taggedCurrentFileContentResult = (0, promptCrafting_1.constructTaggedFile)(currentDocument, editWindowLinesRange, areaAroundEditWindowLinesRange, promptOptions, XtabProvider_1.computeTokens, {
            includeLineNumbers: {
                areaAroundCodeToEdit: xtabPromptOptions.IncludeLineNumbersOption.None,
                currentFileContent: promptOptions.currentFile.includeLineNumbers,
            }
        });
        if (taggedCurrentFileContentResult.isError()) {
            return new statelessNextEditProvider_1.NoNextEditReason.PromptTooLarge('currentFile');
        }
        const { clippedTaggedCurrentDoc, areaAroundCodeToEdit } = taggedCurrentFileContentResult.val;
        telemetry.setNLinesOfCurrentFileInPrompt(clippedTaggedCurrentDoc.lines.length);
        const { aggressivenessLevel, userHappinessScore } = this.userInteractionMonitor.getAggressivenessLevel();
        // Log user's raw aggressiveness setting when explicitly changed from default
        const userAggressivenessSetting = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.Advanced.InlineEditsAggressiveness, this.expService);
        telemetry.setUserAggressivenessSetting(userAggressivenessSetting);
        // Log aggressiveness level and user happiness score
        telemetry.setXtabAggressivenessLevel(aggressivenessLevel);
        if (userHappinessScore !== undefined) {
            telemetry.setXtabUserHappinessScore(userHappinessScore);
        }
        const langCtx = await this.getAndProcessLanguageContext(request, delaySession, activeDocument, cursorPosition, promptOptions, { tracer, logContext, telemetry }, cancellationToken);
        if (cancellationToken.isCancellationRequested) {
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterLanguageContextAwait');
        }
        const neighborSnippets = promptOptions.neighborFiles.enabled
            ? await (0, async_2.raceCancellation)((0, async_2.raceTimeout)(this.similarFilesContextService.getSnippetsForPrompt(activeDocument.id.uri, activeDocument.languageId, activeDocument.documentAfterEdits.value, currentDocument.cursorOffset), delaySession.getDebounceTime()), cancellationToken)
            : undefined;
        if (cancellationToken.isCancellationRequested) {
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterNeighborSnippetsAwait');
        }
        const lintErrors = new lintErrors_1.LintErrors(activeDocument.id, currentDocument, this.langDiagService, request.xtabEditHistory);
        const promptPieces = new promptCrafting_1.PromptPieces(currentDocument, editWindowLinesRange, areaAroundEditWindowLinesRange, activeDocument, request.xtabEditHistory, clippedTaggedCurrentDoc.lines, areaAroundCodeToEdit, langCtx, aggressivenessLevel, lintErrors, XtabProvider_1.computeTokens, promptOptions, neighborSnippets);
        const { prompt: userPrompt, nDiffsInPrompt, diffTokensInPrompt, neighborSnippetsResult } = (0, promptCrafting_1.getUserPrompt)(promptPieces);
        telemetry.setNDiffsInPrompt(nDiffsInPrompt);
        telemetry.setDiffTokensInPrompt(diffTokensInPrompt);
        if (neighborSnippetsResult) {
            telemetry.setNNeighborSnippetsComputed(neighborSnippetsResult.nComputed);
            telemetry.setNNeighborSnippetsInPrompt(neighborSnippetsResult.nIncluded);
            telemetry.setNeighborSnippetIndicesInPrompt(neighborSnippetsResult.includedIndices);
        }
        const responseFormat = xtabPromptOptions.ResponseFormat.fromPromptingStrategy(promptOptions.promptingStrategy);
        const prediction = this.getPredictedOutput(activeDocument, editWindowLines, responseFormat);
        const messages = (0, xtabUtils_1.constructMessages)({
            systemMsg: pickSystemPrompt(promptOptions.promptingStrategy),
            userMsg: userPrompt,
        });
        logContext.setPrompt(messages);
        telemetry.setPrompt(messages);
        const HARD_CHAR_LIMIT = 30000 * 4; // 30K tokens, assuming 4 chars per token -- we use approximation here because counting tokens exactly is time-consuming
        const promptCharCount = (0, xtabUtils_1.charCount)(messages);
        if (promptCharCount > HARD_CHAR_LIMIT) {
            return new statelessNextEditProvider_1.NoNextEditReason.PromptTooLarge('final');
        }
        await this.debounce(delaySession, retryState, tracer, telemetry, cancellationToken);
        if (cancellationToken.isCancellationRequested) {
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterDebounce');
        }
        // Fire-and-forget: collect lint errors and terminal output for telemetry in background to avoid blocking the main path
        Promise.resolve().then(() => {
            const lintErrorsData = lintErrors.getData();
            telemetry.setLintErrors(lintErrorsData);
            logContext.setDiagnosticsData(lintErrorsData);
            const terminalOutputData = this.terminalMonitor.getData();
            telemetry.setTerminalOutput(terminalOutputData);
            logContext.setTerminalData(terminalOutputData);
        });
        // Fire-and-forget: compute GhostText-style similar files context for telemetry
        telemetry.setSimilarFilesContext(this.similarFilesContextService.compute(activeDocument.id.uri, activeDocument.languageId, activeDocument.documentAfterEdits.value, currentDocument.cursorOffset));
        request.fetchIssued = true;
        const editStreamCtx = {
            endpoint,
            modelServiceConfig,
            messages,
            clippedTaggedCurrentDoc,
            editWindowInfo: {
                editWindow,
                editWindowLines,
                cursorOriginalLinesOffset,
                editWindowLineRange: editWindowLinesRange,
            },
            promptPieces,
            prediction,
            originalEditWindow,
        };
        return yield* this.streamEditsWithFiltering(request, editStreamCtx, {
            shouldRemoveCursorTagFromResponse,
            responseFormat,
        }, { aggressivenessLevel, userHappinessScore }, retryState, delaySession, { tracer, logContext, telemetry }, cancellationToken);
    }
    _configureDebounceTimings(request, currentDocument, promptOptions, telemetry, delaySession, tracer) {
        const isCursorAtEndOfLine = currentDocument.isCursorAtEndOfLine();
        telemetry.setIsCursorAtLineEnd(isCursorAtEndOfLine);
        // Apply extra debounce based on cursor position - only one applies
        const isInlineSuggestionPosition = (0, inlineSuggestion_1.determineIsInlineSuggestionPosition)(currentDocument);
        telemetry.setIsInlineSuggestion(!!isInlineSuggestionPosition);
        if (request.isSpeculative) {
            tracer.trace('No extra debounce applied for speculative request');
        }
        else {
            const inlineSuggestionDebounce = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsExtraDebounceInlineSuggestion, this.expService);
            if (isInlineSuggestionPosition && inlineSuggestionDebounce > 0) {
                tracer.trace('Debouncing for inline suggestion position');
                delaySession.setExtraDebounce(inlineSuggestionDebounce);
            }
            else if (isCursorAtEndOfLine) {
                tracer.trace('Debouncing for cursor at end of line');
                delaySession.setExtraDebounce(this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsExtraDebounceEndOfLine, this.expService));
            }
            else {
                tracer.trace('No extra debounce applied');
            }
        }
        // Adjust debounce based on user aggressiveness setting for non-aggressiveness models
        if (!(0, xtabPromptOptions_1.isAggressivenessStrategy)(promptOptions.promptingStrategy)) {
            this._applyAggressivenessSettings(delaySession, tracer);
        }
    }
    _applyAggressivenessSettings(delaySession, tracer) {
        const userAggressiveness = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.Advanced.InlineEditsAggressiveness, this.expService);
        const configsByLevel = {
            [xtabPromptOptions_1.AggressivenessSetting.Low]: { minResponseConfigKey: configurationService_1.ConfigKey.TeamInternal.InlineEditsAggressivenessLowMinResponseTimeMs },
            [xtabPromptOptions_1.AggressivenessSetting.Medium]: { minResponseConfigKey: configurationService_1.ConfigKey.TeamInternal.InlineEditsAggressivenessMediumMinResponseTimeMs },
            [xtabPromptOptions_1.AggressivenessSetting.High]: { debounceConfigKey: configurationService_1.ConfigKey.TeamInternal.InlineEditsAggressivenessHighDebounceMs },
            [xtabPromptOptions_1.AggressivenessSetting.Default]: undefined,
        };
        const entry = configsByLevel[userAggressiveness];
        if (!entry) {
            return;
        }
        // Apply debounce override if configured for this level
        if (entry.debounceConfigKey) {
            const debounceMs = this.configService.getExperimentBasedConfig(entry.debounceConfigKey, this.expService);
            delaySession.setBaseDebounceTime(debounceMs);
            tracer.trace(`Aggressiveness ${userAggressiveness}: debounce set to ${debounceMs}ms`);
        }
        // Apply min response time if configured for this level
        if (entry.minResponseConfigKey) {
            // Skip min response time delay if the user just accepted a suggestion
            if (this.userInteractionMonitor.wasLastActionAcceptance) {
                tracer.trace(`Aggressiveness ${userAggressiveness}: skipping min response time (last action was acceptance)`);
                return;
            }
            const minResponseTimeMs = this.configService.getExperimentBasedConfig(entry.minResponseConfigKey, this.expService);
            delaySession.setExpectedTotalTime(minResponseTimeMs);
            tracer.trace(`Aggressiveness ${userAggressiveness}: min response time set to ${minResponseTimeMs}ms`);
        }
    }
    getAndProcessLanguageContext(request, delaySession, activeDocument, cursorPosition, promptOptions, tracing, cancellationToken) {
        const recordingEnabled = this.configService.getConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsLogContextRecorderEnabled);
        if (!promptOptions.languageContext.enabled && !recordingEnabled) {
            return Promise.resolve(undefined);
        }
        const langCtxPromise = this.getLanguageContext(request, delaySession, activeDocument, cursorPosition, tracing, cancellationToken);
        // if recording, add diagnostics for the file to the recording and hook up the language context promise to write to the recording
        if (recordingEnabled) {
            langCtxPromise.then(langCtxs => {
                if (langCtxs) {
                    tracing.logContext.setLanguageContext(langCtxs);
                }
            });
        }
        return promptOptions.languageContext.enabled
            ? langCtxPromise
            : Promise.resolve(undefined);
    }
    async getLanguageContext(request, delaySession, activeDocument, cursorPosition, tracing, cancellationToken) {
        try {
            const textDoc = this.workspaceService.textDocuments.find(doc => doc.uri.toString() === activeDocument.id.uri);
            if (textDoc === undefined) {
                return undefined;
            }
            const providers = this.langCtxService.getContextProviders(textDoc, languageContextProviderService_1.ProviderTarget.NES);
            if (providers.length < 1) {
                return undefined;
            }
            const debounceTime = delaySession.getDebounceTime();
            const cursorPositionVscode = new vscodeTypesShim_1.Position(cursorPosition.lineNumber - 1, cursorPosition.column - 1);
            const ctxRequest = {
                opportunityId: request.opportunityId,
                completionId: request.headerRequestId,
                documentContext: {
                    uri: textDoc.uri.toString(),
                    languageId: textDoc.languageId,
                    version: textDoc.version,
                    offset: textDoc.offsetAt(cursorPositionVscode),
                    position: cursorPositionVscode
                },
                activeExperiments: new Map(),
                timeBudget: debounceTime,
                timeoutEnd: Date.now() + debounceTime,
                source: 'nes',
            };
            const isSnippetIgnored = async (item) => {
                const uris = [item.uri, ...(item.additionalUris ?? [])];
                const isIgnored = await (0, async_1.raceFilter)(uris.map(uri => this.ignoreService.isCopilotIgnored(uri)), r => r);
                return !!isIgnored;
            };
            const langCtxItems = [];
            const getContextPromise = async () => {
                const ctxIter = this.langCtxService.getContextItems(textDoc, ctxRequest, cancellationToken);
                for await (const item of ctxIter) {
                    if (item.kind === languageContextService_1.ContextKind.Snippet && await isSnippetIgnored(item)) {
                        // If the snippet is ignored, we don't want to include it in the context
                        continue;
                    }
                    langCtxItems.push({ context: item, timeStamp: Date.now(), onTimeout: false });
                }
            };
            const start = Date.now();
            await (0, async_2.raceCancellation)((0, async_2.raceTimeout)(getContextPromise(), debounceTime), cancellationToken);
            if (cancellationToken.isCancellationRequested) {
                return undefined;
            }
            const end = Date.now();
            const langCtxOnTimeout = this.langCtxService.getContextItemsOnTimeout(textDoc, ctxRequest);
            for (const item of langCtxOnTimeout) {
                if (item.kind === languageContextService_1.ContextKind.Snippet && await isSnippetIgnored(item)) {
                    // If the snippet is ignored, we don't want to include it in the context
                    continue;
                }
                langCtxItems.push({ context: item, timeStamp: end, onTimeout: true });
            }
            return { start, end, items: langCtxItems };
        }
        catch (error) {
            tracing.logContext.setError(errors_1.ErrorUtils.fromUnknown(error));
            tracing.tracer.trace(`Failed to fetch language context: ${error}`);
            return undefined;
        }
    }
    async *streamEditsWithFiltering(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, tracing, cancellationToken) {
        const tracer = tracing.tracer.createSubLogger('streamEditsWithFiltering');
        const subTracing = { ...tracing, tracer };
        const iterator = this.streamEdits(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, subTracing, cancellationToken);
        let nEdits = 0;
        let r = await iterator.next();
        while (!r.done) {
            const edit = r.value.edit;
            const [filteredEdits, filterNames] = this.filterEdit(request.getActiveDocument(), [edit]);
            const isFilteredOut = filteredEdits.length === 0;
            if (isFilteredOut) {
                tracer.trace(`Filtered out an edit: ${edit.toString()} using ${filterNames.join(', ')} filter(s)`);
            }
            else {
                tracer.trace(`Yielding an edit: ${edit.toString()}`);
                yield r.value;
                nEdits++;
            }
            r = await iterator.next();
        }
        if (nEdits === 0 &&
            r.value instanceof statelessNextEditProvider_1.NoNextEditReason.NoSuggestions // only retry if there was no error, cancellation, etc.
        ) {
            return yield* this.doGetNextEditsWithCursorJump(request, editStreamCtx, delaySession, tracing, cancellationToken, retryState);
        }
        return r.value;
    }
    async *streamEdits(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, tracing, cancellationToken) {
        const tracer = tracing.tracer.createSubLogger('streamEdits');
        // Create a local cancellation source linked to the caller's token.
        // This lets us cancel the fetch immediately on cursor-line divergence
        // without reaching into the request's CancellationTokenSource (which
        // is owned by nextEditProvider.ts).
        const fetchCts = new cancellation_1.CancellationTokenSource(cancellationToken);
        const fetchCancellationToken = fetchCts.token;
        try {
            return yield* this._streamEditsImpl(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, { ...tracing, tracer }, cancellationToken, fetchCts, fetchCancellationToken);
        }
        finally {
            fetchCts.dispose();
        }
    }
    /**
     * Initiates the HTTP fetch, sets up the streaming pipeline, and returns either
     * a clean line stream (with cursor-tag removal and latency logging applied)
     * or an error / retry signal.
     *
     * This method encapsulates all fetch infrastructure so that downstream response
     * format handlers only need an `AsyncIterable<string>` line stream.
     */
    async _performFetch(endpoint, messages, prediction, requestId, fetchMetadata, shouldRemoveCursorTagFromResponse, editWindow, documentBeforeEdits, fetchCancellationToken, tracing) {
        const { tracer, logContext, telemetry } = tracing;
        const useFetcher = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.NextEditSuggestionsFetcher, this.expService) || undefined;
        const fetchStreamSource = new chatMLFetcher_1.FetchStreamSource();
        const fetchRequestStopWatch = new stopwatch_1.StopWatch();
        let responseSoFar = '';
        let ttft;
        const firstTokenReceived = new async_2.DeferredPromise();
        logContext.setHeaderRequestId(requestId);
        telemetry.setFetchStartedAt();
        logContext.setFetchStartTime();
        // we must not await this promise because we want to stream edits as they come in
        const fetchResultPromise = endpoint.makeChatRequest2({
            debugName: XtabProvider_1.ID,
            messages,
            finishedCb: async (text, _, delta) => {
                if (!firstTokenReceived.isSettled) {
                    firstTokenReceived.complete();
                }
                if (ttft === undefined && text !== '') {
                    ttft = fetchRequestStopWatch.elapsed();
                    logContext.addLog(`TTFT ${ttft} ms`);
                }
                fetchStreamSource.update(text, delta);
                responseSoFar = text;
                logContext.setResponse(responseSoFar);
                return undefined;
            },
            location: commonTypes_1.ChatLocation.Other,
            source: undefined,
            requestOptions: {
                temperature: 0,
                stream: true,
                prediction,
            },
            userInitiatedRequest: undefined,
            telemetryProperties: {
                requestId,
            },
            useFetcher,
            customMetadata: {
                aggressivenessLevel: fetchMetadata.aggressivenessLevel,
                userHappinessScore: fetchMetadata.userHappinessScore,
            },
        }, fetchCancellationToken);
        telemetry.setResponse(fetchResultPromise.then((response) => ({ response, ttft })));
        logContext.setFullResponse(fetchResultPromise.then((response) => response.type === commonTypes_1.ChatFetchResponseType.Success ? response.value : undefined));
        fetchResultPromise
            .then((response) => {
            if (response.type !== commonTypes_1.ChatFetchResponseType.Success) {
                fetchStreamSource.reject(new fetchStreamError_1.FetchStreamError(mapChatFetcherErrorToNoNextEditReason(response)));
            }
            else {
                fetchStreamSource.resolve();
            }
        })
            .catch((err) => {
            // in principle this shouldn't happen because ChatMLFetcher's fetchOne should not throw
            logContext.setError(errors_1.ErrorUtils.fromUnknown(err));
            logContext.addLog(`ChatMLFetcher fetch call threw -- this's UNEXPECTED!`);
            fetchStreamSource.reject(errors_1.ErrorUtils.fromUnknown(err));
        }).finally(() => {
            logContext.setFetchEndTime();
            if (!firstTokenReceived.isSettled) {
                firstTokenReceived.complete();
            }
            logContext.setResponse(responseSoFar);
        });
        const fetchRes = await Promise.race([firstTokenReceived.p, fetchResultPromise]);
        if (fetchRes && fetchRes.type !== commonTypes_1.ChatFetchResponseType.Success) {
            if (fetchRes.type === commonTypes_1.ChatFetchResponseType.NotFound &&
                !this.forceUseDefaultModel // if we haven't already forced using the default model; otherwise, this could cause an infinite loop
            ) {
                this.forceUseDefaultModel = true;
                return FetchResult.ModelNotFound.INSTANCE;
            }
            // diff-patch based model returns no choices if it has no edits to suggest
            if (fetchRes.type === commonTypes_1.ChatFetchResponseType.Unknown && fetchRes.reason === commonTypes_1.RESPONSE_CONTAINED_NO_CHOICES) {
                return new FetchResult.FetchFailure(new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(documentBeforeEdits, editWindow));
            }
            return new FetchResult.FetchFailure(mapChatFetcherErrorToNoNextEditReason(fetchRes));
        }
        const llmLinesStream = asyncIterableUtils_1.AsyncIterUtilsExt.splitLines(asyncIterableUtils_1.AsyncIterUtils.map(fetchStreamSource.stream, (chunk) => chunk.delta.text));
        // logging of times
        // removal of cursor tag if option is set
        const linesStream = (async function* () {
            let i = 0;
            for await (const v of llmLinesStream) {
                const trace = `Line ${i++} emitted with latency ${fetchRequestStopWatch.elapsed()} ms`;
                tracer.trace(trace);
                yield shouldRemoveCursorTagFromResponse
                    ? v.replaceAll(tags_1.PromptTags.CURSOR, '')
                    : v;
            }
        })();
        return new FetchResult.Lines(linesStream, () => responseSoFar, fetchRequestStopWatch);
    }
    async *_streamEditsImpl(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, tracing, cancellationToken, fetchCts, fetchCancellationToken) {
        const { tracer, logContext, telemetry } = tracing;
        const { endpoint, messages, clippedTaggedCurrentDoc, editWindowInfo, promptPieces, prediction, originalEditWindow } = editStreamCtx;
        const { editWindow, editWindowLines, cursorOriginalLinesOffset, editWindowLineRange } = editWindowInfo;
        const targetDocument = request.getActiveDocument().id;
        // Phase 1: Fetch lifecycle — initiate HTTP request and produce a clean line stream
        const fetchResult = await this._performFetch(endpoint, messages, prediction, request.headerRequestId, fetchMetadata, responseOpts.shouldRemoveCursorTagFromResponse, editWindow, request.documentBeforeEdits, fetchCancellationToken, tracing);
        if (fetchResult instanceof FetchResult.ModelNotFound) {
            return yield* this.doGetNextEdit(request, delaySession, tracing, cancellationToken, retryState);
        }
        if (fetchResult instanceof FetchResult.FetchFailure) {
            return fetchResult.reason;
        }
        const { linesStream, getResponseSoFar, fetchRequestStopWatch } = fetchResult;
        // Phase 2: Dispatch to the appropriate response format handler
        const isFromCursorJump = retryState instanceof RetryState.Retrying && retryState.reason === 'cursorJump';
        let parseResult;
        try {
            switch (responseOpts.responseFormat) {
                case xtabPromptOptions.ResponseFormat.EditWindowOnly: {
                    parseResult = (0, responseFormatHandlers_1.handleEditWindowOnly)(linesStream);
                    break;
                }
                case xtabPromptOptions.ResponseFormat.CodeBlock: {
                    parseResult = (0, responseFormatHandlers_1.handleCodeBlock)(linesStream);
                    break;
                }
                case xtabPromptOptions.ResponseFormat.EditWindowWithEditIntent:
                case xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort: {
                    const parseMode = responseOpts.responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort
                        ? editIntent_1.EditIntentParseMode.ShortName
                        : editIntent_1.EditIntentParseMode.Tags;
                    parseResult = await (0, responseFormatHandlers_1.handleEditWindowWithEditIntent)(linesStream, tracer, parseMode);
                    break;
                }
                case xtabPromptOptions.ResponseFormat.CustomDiffPatch: {
                    const activeDoc = request.getActiveDocument();
                    const currentDocument = promptPieces.currentDocument;
                    const lastLine = currentDocument.lines[clippedTaggedCurrentDoc.keptRange.endExclusive - 1];
                    const lastLineLength = lastLine.length;
                    const pseudoEditWindow = currentDocument.transformer.getOffsetRange(new range_1.Range(clippedTaggedCurrentDoc.keptRange.start + 1, 1, clippedTaggedCurrentDoc.keptRange.endExclusive, lastLineLength + 1));
                    const duplicateAdditionsMode = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabDuplicateAdditionsMode, this.expService);
                    parseResult = new responseFormatHandlers_1.ResponseParseResult.DirectEdits(xtabCustomDiffPatchResponseHandler_1.XtabCustomDiffPatchResponseHandler.handleResponse(linesStream, currentDocument, activeDoc.id, activeDoc.workspaceRoot, pseudoEditWindow, tracer, duplicateAdditionsMode));
                    break;
                }
                case xtabPromptOptions.ResponseFormat.UnifiedWithXml: {
                    parseResult = await (0, responseFormatHandlers_1.handleUnifiedWithXml)(linesStream, {
                        editWindowLines,
                        editWindowLineRange,
                        cursorOriginalLinesOffset,
                        cursorColumnZeroBased: promptPieces.currentDocument.cursorPosition.column - 1,
                        editWindow,
                        originalEditWindow,
                        targetDocument,
                        isFromCursorJump,
                    }, request.documentBeforeEdits, tracer);
                    break;
                }
                default:
                    (0, assert_1.assertNever)(responseOpts.responseFormat);
            }
            // Handle result uniformly
            if (parseResult instanceof responseFormatHandlers_1.ResponseParseResult.Done) {
                return parseResult.reason;
            }
            if (parseResult instanceof responseFormatHandlers_1.ResponseParseResult.DirectEdits) {
                return yield* parseResult.stream;
            }
            // parseResult is EditWindowLines — log edit-intent telemetry and apply aggressiveness filter
            if (parseResult.editIntentMetadata) {
                const { intent, parseError } = parseResult.editIntentMetadata;
                telemetry.setEditIntent(intent);
                if (parseError) {
                    telemetry.setEditIntentParseError(parseError);
                }
                if (!xtabPromptOptions.EditIntent.shouldShowEdit(intent, promptPieces.aggressivenessLevel)) {
                    tracer.trace(`Filtered out edit due to edit intent "${intent}" with aggressiveness "${promptPieces.aggressivenessLevel}"`);
                    return new statelessNextEditProvider_1.NoNextEditReason.FilteredOut(`editIntent:${intent} aggressivenessLevel:${promptPieces.aggressivenessLevel}`);
                }
            }
            const cleanedLinesStream = parseResult.lines;
            const diffOptions = {
                emitFastCursorLineChange: responseProcessor_1.ResponseProcessor.mapEmitFastCursorLineChange(this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderEmitFastCursorLineChange, this.expService)),
                nLinesToConverge: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabNNonSignificantLinesToConverge, this.expService),
                nSignificantLinesToConverge: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabNSignificantLinesToConverge, this.expService),
            };
            tracer.trace(`starting to diff stream against edit window lines with latency ${fetchRequestStopWatch.elapsed()} ms`);
            // Wrap the line stream to detect early divergence between the user's
            // intermediate edits and the model's streamed output.
            // In `Cursor` mode only the cursor line is checked; in `EditWindow`
            // mode every line in the edit window is checked.
            //
            // We check compatibility using `isModelLineCompatible`: the user's
            // line change must be contained within the model's line change range
            // and match via the helper's `startsWith` / auto-close subsequence rules.
            const earlyDivergenceMode = (0, backwardCompatSetting_1.backwardCompatSetting)(this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabEarlyCursorLineDivergenceCancellation, this.expService), (value) => {
                switch (value) {
                    case false:
                    case undefined:
                        return xtabPromptOptions_1.EarlyDivergenceCancellationMode.Off;
                    case true:
                        return xtabPromptOptions_1.EarlyDivergenceCancellationMode.Cursor;
                    case xtabPromptOptions_1.EarlyDivergenceCancellationMode.Off:
                    case xtabPromptOptions_1.EarlyDivergenceCancellationMode.Cursor:
                    case xtabPromptOptions_1.EarlyDivergenceCancellationMode.EditWindow:
                        return value;
                    default:
                        return xtabPromptOptions_1.EarlyDivergenceCancellationMode.Off;
                }
            });
            let lineDiverged = false;
            const divergenceCheckedStream = earlyDivergenceMode === xtabPromptOptions_1.EarlyDivergenceCancellationMode.Off
                ? cleanedLinesStream
                : linesWithIntermediateEditDivergenceCheck(cleanedLinesStream, cursorOriginalLinesOffset, request, editWindowLineRange, editWindowLines, fetchCts, tracing, (value) => { lineDiverged = value; }, earlyDivergenceMode);
            let i = 0;
            let hasBeenDelayed = false;
            for await (const edit of responseProcessor_1.ResponseProcessor.diff(editWindowLines, divergenceCheckedStream, cursorOriginalLinesOffset, diffOptions)) {
                if (lineDiverged) {
                    break;
                }
                tracer.trace(`ResponseProcessor streamed edit #${i} with latency ${fetchRequestStopWatch.elapsed()} ms`);
                const singleLineEdits = [];
                if (edit.lineRange.startLineNumber === edit.lineRange.endLineNumberExclusive || // we don't want to run diff on insertion
                    edit.newLines.length === 0 || // we don't want to run diff on deletion
                    edit.lineRange.endLineNumberExclusive - edit.lineRange.startLineNumber === 1 && edit.newLines.length === 1 // we want to run diff on single line edits
                ) {
                    const singleLineEdit = new lineEdit_1.LineReplacement(new lineRange_1.LineRange(edit.lineRange.startLineNumber + editWindowLineRange.start, edit.lineRange.endLineNumberExclusive + editWindowLineRange.start), edit.newLines);
                    singleLineEdits.push(singleLineEdit);
                }
                else {
                    const affectedOriginalLines = editWindowLines.slice(edit.lineRange.startLineNumber - 1, edit.lineRange.endLineNumberExclusive - 1).join('\n');
                    const diffResult = await this.diffService.computeDiff(affectedOriginalLines, edit.newLines.join('\n'), {
                        ignoreTrimWhitespace: false,
                        maxComputationTimeMs: 0,
                        computeMoves: false
                    });
                    tracer.trace(`Ran diff for #${i} with latency ${fetchRequestStopWatch.elapsed()} ms`);
                    const translateByNLines = editWindowLineRange.start + edit.lineRange.startLineNumber;
                    for (const change of diffResult.changes) {
                        const singleLineEdit = new lineEdit_1.LineReplacement(new lineRange_1.LineRange(translateByNLines + change.original.startLineNumber - 1, translateByNLines + change.original.endLineNumberExclusive - 1), edit.newLines.slice(change.modified.startLineNumber - 1, change.modified.endLineNumberExclusive - 1));
                        singleLineEdits.push(singleLineEdit);
                    }
                }
                logContext.setResponse(getResponseSoFar());
                for (const singleLineEdit of singleLineEdits) {
                    tracer.trace(`extracting edit #${i}: ${singleLineEdit.toString()}`);
                    if (!hasBeenDelayed) { // delay only the first one
                        hasBeenDelayed = true;
                        const artificialDelay = this.determineArtificialDelayMs(delaySession, tracer, telemetry);
                        if (artificialDelay) {
                            await (0, async_2.timeout)(artificialDelay);
                            tracer.trace(`Artificial delay of ${artificialDelay} ms completed`);
                            if (cancellationToken.isCancellationRequested) {
                                return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterArtificialDelay');
                            }
                        }
                    }
                    yield { edit: singleLineEdit, isFromCursorJump, window: editWindow, originalWindow: originalEditWindow, targetDocument };
                    i++;
                }
            }
            if (lineDiverged) {
                return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled(earlyDivergenceMode === xtabPromptOptions_1.EarlyDivergenceCancellationMode.Cursor ? 'cursorLineDiverged' : 'editWindowLineDiverged');
            }
            return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
        }
        catch (err) {
            if (err instanceof fetchStreamError_1.FetchStreamError) {
                return err.reason;
            }
            logContext.setError(err);
            return new statelessNextEditProvider_1.NoNextEditReason.Unexpected(errors_1.ErrorUtils.fromUnknown(err));
        }
    }
    async *doGetNextEditsWithCursorJump(request, editStreamCtx, delaySession, tracing, cancellationToken, retryState) {
        const { tracer, telemetry } = tracing;
        const { editWindowInfo: { editWindow }, modelServiceConfig, promptPieces } = editStreamCtx;
        const noSuggestions = new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
        const nextCursorLinePrediction = this.nextCursorPredictor.determineEnablement(modelServiceConfig.supportsNextCursorLinePrediction);
        if (nextCursorLinePrediction === undefined || retryState instanceof RetryState.Retrying) {
            return noSuggestions;
        }
        if (hasUserTypedSinceRequestStarted(request)) {
            tracer.trace('Skipping cursor prediction: user typed during request');
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('beforeNextCursorPredictionFetchUserTyped');
        }
        const nextCursorLineR = await this.nextCursorPredictor.predictNextCursorPosition(promptPieces, tracer, telemetry, cancellationToken);
        if (cancellationToken.isCancellationRequested) {
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterNextCursorPredictionFetch');
        }
        if (hasUserTypedSinceRequestStarted(request)) {
            tracer.trace('Skipping cursor prediction: user typed during prediction fetch');
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterNextCursorPredictionFetchUserTyped');
        }
        if (nextCursorLineR.isError()) {
            tracer.trace(`Predicted next cursor line error: ${nextCursorLineR.err.message}`);
            telemetry.setNextCursorLineError(nextCursorLineR.err.message);
            return noSuggestions;
        }
        const prediction = nextCursorLineR.val;
        if (prediction.kind === 'differentFile') {
            return yield* this.handleCrossFilePrediction(prediction, nextCursorLinePrediction, request, editStreamCtx, delaySession, tracing, cancellationToken);
        }
        const nextCursorLineZeroBased = prediction.lineNumber;
        const lineDistanceFromCursorLine = nextCursorLineZeroBased - promptPieces.currentDocument.cursorLineOffset;
        telemetry.setNextCursorLineDistance(lineDistanceFromCursorLine);
        telemetry.setNextCursorIsCrossFile(false);
        tracer.trace(`Predicted next cursor line: ${nextCursorLineZeroBased}`);
        if (nextCursorLineZeroBased >= promptPieces.currentDocument.lines.length) { // >= because the line index is zero-based
            tracer.trace(`Predicted next cursor line error: exceedsDocumentLines`);
            telemetry.setNextCursorLineError('exceedsDocumentLines');
            return noSuggestions;
        }
        if (promptPieces.editWindowLinesRange.contains(nextCursorLineZeroBased)) {
            tracer.trace(`Predicted next cursor line error: withinEditWindow`);
            telemetry.setNextCursorLineError('withinEditWindow');
            return noSuggestions;
        }
        const nextCursorLineOneBased = nextCursorLineZeroBased + 1;
        const nextCursorLine = promptPieces.activeDoc.documentAfterEditsLines.at(nextCursorLineZeroBased);
        const nextCursorColumn = XtabProvider_1.getNextCursorColumn(nextCursorLine);
        switch (nextCursorLinePrediction) {
            case nextCursorLinePrediction_1.NextCursorLinePrediction.Jump: {
                const nextCursorPosition = new position_1.Position(nextCursorLineOneBased, nextCursorColumn);
                return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow, nextCursorPosition);
            }
            case nextCursorLinePrediction_1.NextCursorLinePrediction.OnlyWithEdit: {
                const v = this.doGetNextEditWithSelection(request, new range_1.Range(nextCursorLineOneBased, nextCursorColumn, nextCursorLineOneBased, nextCursorColumn), delaySession, tracing, cancellationToken, new RetryState.Retrying('cursorJump'), editWindow);
                return yield* v;
            }
            default: {
                (0, assert_1.assertNever)(nextCursorLinePrediction);
            }
        }
    }
    async *handleCrossFilePrediction(prediction, nextCursorLinePrediction, request, editStreamCtx, delaySession, tracing, cancellationToken) {
        const { tracer, telemetry } = tracing;
        const { editWindowInfo: { editWindow }, promptPieces } = editStreamCtx;
        const workspaceRoot = promptPieces.activeDoc.workspaceRoot;
        if (!workspaceRoot && !(0, path_1.isAbsolute)(prediction.filePath)) {
            tracer.trace('Predicted cross-file cursor jump error: noWorkspaceRoot');
            telemetry.setNextCursorLineError('crossFile:noWorkspaceRoot');
            return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
        }
        const targetUri = (0, path_1.isAbsolute)(prediction.filePath)
            ? uri_1.URI.file(prediction.filePath)
            : uri_1.URI.joinPath(workspaceRoot, prediction.filePath);
        const targetDocumentId = documentId_1.DocumentId.create(targetUri.toString());
        const nextCursorLineOneBased = prediction.lineNumber + 1;
        const nextCursorPosition = new position_1.Position(nextCursorLineOneBased, 1);
        telemetry.setNextCursorIsCrossFile(true);
        tracer.trace(`Predicted cross-file cursor jump: ${prediction.filePath}:${prediction.lineNumber}`);
        switch (nextCursorLinePrediction) {
            case nextCursorLinePrediction_1.NextCursorLinePrediction.Jump: {
                return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow, nextCursorPosition, targetDocumentId);
            }
            case nextCursorLinePrediction_1.NextCursorLinePrediction.OnlyWithEdit: {
                let targetTextDoc;
                try {
                    targetTextDoc = await this.workspaceService.openTextDocument(targetUri);
                }
                catch (err) {
                    tracer.trace(`Failed to open target file for cross-file edit: ${errors_1.ErrorUtils.fromUnknown(err).message}`);
                    telemetry.setNextCursorLineError('crossFile:failedToOpenFile');
                    return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow, nextCursorPosition, targetDocumentId);
                }
                if (cancellationToken.isCancellationRequested) {
                    return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterCrossFileOpenTextDocument');
                }
                if (hasUserTypedSinceRequestStarted(request)) {
                    tracer.trace('Skipping cross-file edit: user typed during openTextDocument');
                    return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterCrossFileOpenTextDocumentUserTyped');
                }
                const targetContent = new abstractText_1.StringText(targetTextDoc.getText());
                const syntheticDoc = new statelessNextEditProvider_1.StatelessNextEditDocument(targetDocumentId, promptPieces.activeDoc.workspaceRoot, languageId_1.LanguageId.create(targetTextDoc.languageId), targetContent.getLines(), lineEdit_1.LineEdit.empty, targetContent, new edit_1.Edits(stringEdit_1.StringEdit, []));
                const syntheticRequest = new statelessNextEditProvider_1.StatelessNextEditRequest(request.headerRequestId, request.opportunityId, targetContent, [syntheticDoc], 0, request.xtabEditHistory, new async_2.DeferredPromise(), request.expandedEditWindowNLines, request.isSpeculative, request.logContext, request.recordingBookmark, request.recording, request.providerRequestStartDateTime);
                return yield* this.doGetNextEditWithSelection(syntheticRequest, new range_1.Range(nextCursorLineOneBased, 1, nextCursorLineOneBased, 1), delaySession, tracing, cancellationToken, new RetryState.Retrying('cursorJump'), editWindow);
            }
            default: {
                (0, assert_1.assertNever)(nextCursorLinePrediction);
            }
        }
    }
    computeEditWindowLinesRange(currentDocument, request, tracer, telemetry) {
        const currentDocLines = currentDocument.lines;
        const cursorLineOffset = currentDocument.cursorLineOffset;
        let nLinesAbove;
        {
            const useVaryingLinesAbove = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderUseVaryingLinesAbove, this.expService);
            if (useVaryingLinesAbove) {
                nLinesAbove = 0; // default
                for (let i = 0; i < 8; ++i) {
                    const lineIdx = cursorLineOffset - i;
                    if (lineIdx < 0) {
                        break;
                    }
                    if (currentDocLines[lineIdx].trim() !== '') {
                        nLinesAbove = i;
                        break;
                    }
                }
            }
            else {
                nLinesAbove = (this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderNLinesAbove, this.expService)
                    ?? promptCrafting_1.N_LINES_ABOVE);
            }
        }
        let nLinesBelow;
        if (request.expandedEditWindowNLines !== undefined) {
            tracer.trace(`Using expanded nLinesBelow: ${request.expandedEditWindowNLines}`);
            nLinesBelow = request.expandedEditWindowNLines;
        }
        else {
            const overriddenNLinesBelow = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderNLinesBelow, this.expService);
            if (overriddenNLinesBelow !== undefined) {
                tracer.trace(`Using overridden nLinesBelow: ${overriddenNLinesBelow}`);
                nLinesBelow = overriddenNLinesBelow;
            }
            else {
                tracer.trace(`Using default nLinesBelow: ${promptCrafting_1.N_LINES_BELOW}`);
                nLinesBelow = promptCrafting_1.N_LINES_BELOW; // default
            }
        }
        let codeToEditStart = Math.max(0, cursorLineOffset - nLinesAbove);
        let codeToEditEndExcl = Math.min(currentDocLines.length, cursorLineOffset + nLinesBelow + 1);
        const maxMergeConflictLines = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabMaxMergeConflictLines, this.expService);
        if (maxMergeConflictLines) {
            const tentativeEditWindow = new offsetRange_1.OffsetRange(codeToEditStart, codeToEditEndExcl);
            const mergeConflictRange = (0, xtabUtils_1.findMergeConflictMarkersRange)(currentDocLines, tentativeEditWindow, maxMergeConflictLines);
            if (mergeConflictRange) {
                const onlyMergeConflictLines = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabOnlyMergeConflictLines, this.expService);
                telemetry.setMergeConflictExpanded(onlyMergeConflictLines ? 'only' : 'normal');
                if (onlyMergeConflictLines) {
                    tracer.trace(`Expanding edit window to include ONLY merge conflict markers: ${mergeConflictRange.toString()}`);
                    codeToEditStart = mergeConflictRange.start;
                    codeToEditEndExcl = mergeConflictRange.endExclusive;
                }
                else {
                    tracer.trace(`Expanding edit window to include merge conflict markers: ${mergeConflictRange.toString()}; edit window range [${codeToEditStart}, ${codeToEditEndExcl})`);
                    codeToEditEndExcl = Math.max(codeToEditEndExcl, mergeConflictRange.endExclusive);
                }
            }
        }
        return new offsetRange_1.OffsetRange(codeToEditStart, codeToEditEndExcl);
    }
    determineModelConfiguration(activeDocument) {
        if (this.forceUseDefaultModel) {
            const defaultOptions = {
                modelName: undefined,
                ...xtabPromptOptions.DEFAULT_OPTIONS,
            };
            const defaultModelConfig = this.modelService.defaultModelConfiguration();
            return {
                promptOptions: overrideModelConfig(defaultOptions, defaultModelConfig),
                modelServiceConfig: defaultModelConfig
            };
        }
        const sourcedModelConfig = {
            modelName: undefined,
            promptingStrategy: undefined,
            currentFile: {
                maxTokens: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabCurrentFileMaxTokens, this.expService),
                includeTags: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabIncludeTagsInCurrentFile, this.expService),
                includeLineNumbers: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabIncludeLineNumbersInCurrentFile, this.expService),
                includeCursorTag: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabIncludeCursorTagInCurrentFile, this.expService),
                prioritizeAboveCursor: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabPrioritizeAboveCursor, this.expService)
            },
            pagedClipping: {
                pageSize: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabPageSize, this.expService)
            },
            recentlyViewedDocuments: {
                nDocuments: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabNRecentlyViewedDocuments, this.expService),
                maxTokens: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabRecentlyViewedDocumentsMaxTokens, this.expService),
                includeViewedFiles: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabIncludeViewedFiles, this.expService),
                includeLineNumbers: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabRecentlyViewedIncludeLineNumbers, this.expService),
                clippingStrategy: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabRecentlyViewedClippingStrategy, this.expService),
            },
            languageContext: determineLanguageContextOptions(activeDocument.languageId, {
                enabled: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabLanguageContextEnabled, this.expService),
                enabledLanguages: this.configService.getConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabLanguageContextEnabledLanguages),
                enableAllContextProviders: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.Advanced.DiagnosticsContextProvider, this.expService)
                    || this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.Advanced.ChatSessionContextProvider, this.expService),
                maxTokens: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabLanguageContextMaxTokens, this.expService),
                traitPosition: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabLanguageContextTraitsPosition, this.expService),
            }),
            neighborFiles: {
                enabled: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabIncludeNeighborFiles, this.expService),
                maxTokens: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabNeighborFilesMaxTokens, this.expService),
            },
            diffHistory: {
                nEntries: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabDiffNEntries, this.expService),
                maxTokens: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabDiffMaxTokens, this.expService),
                onlyForDocsInPrompt: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabDiffOnlyForDocsInPrompt, this.expService),
                useRelativePaths: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabDiffUseRelativePaths, this.expService),
            },
            lintOptions: undefined,
            includePostScript: true,
            globalBudget: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabGlobalBudgetEnabled, this.expService)
                ? {
                    totalTokens: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabGlobalBudgetTotalTokens, this.expService),
                    order: xtabPromptOptions.GlobalBudgetOptions.DEFAULT_ORDER,
                    shares: xtabPromptOptions.GlobalBudgetOptions.DEFAULT_SHARES,
                }
                : undefined,
        };
        const selectedModelConfig = this.modelService.selectedModelConfiguration();
        // proxy /models doesn't know about includeTagsInCurrentFile field as of now, so hard code it to true for CopilotNesXtab strategy
        const modelConfig = selectedModelConfig.promptingStrategy === xtabPromptOptions.PromptingStrategy.CopilotNesXtab
            ? { ...selectedModelConfig, includeTagsInCurrentFile: true }
            : selectedModelConfig;
        return {
            promptOptions: overrideModelConfig(sourcedModelConfig, modelConfig),
            modelServiceConfig: modelConfig
        };
    }
    getEndpointWithLogging(configuredModelName, logContext, telemetry) {
        const endpoint = this.getEndpoint(configuredModelName);
        logContext.setEndpointInfo(typeof endpoint.urlOrRequestMetadata === 'string' ? endpoint.urlOrRequestMetadata : JSON.stringify(endpoint.urlOrRequestMetadata.type), endpoint.model);
        telemetry.setModelName(endpoint.model);
        return endpoint;
    }
    getEndpoint(configuredModelName) {
        const url = this.configService.getConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderUrl);
        const apiKey = this.configService.getConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderApiKey);
        const hasOverriddenUrlAndApiKey = url !== undefined && apiKey !== undefined;
        if (hasOverriddenUrlAndApiKey) {
            return this.instaService.createInstance(xtabEndpoint_1.XtabEndpoint, url, apiKey, configuredModelName);
        }
        return (0, proxyXtabEndpoint_1.createProxyXtabEndpoint)(this.instaService, configuredModelName);
    }
    getPredictedOutput(doc, editWindowLines, responseFormat) {
        return this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderUsePrediction, this.expService)
            ? {
                type: 'content',
                content: getPredictionContents(doc, editWindowLines, responseFormat)
            }
            : undefined;
    }
    async debounce(delaySession, retryState, logger, telemetry, cancellationToken) {
        if (this.simulationCtx.isInSimulationTests) {
            return;
        }
        if (retryState instanceof RetryState.Retrying) {
            logger.trace('Skipping debounce on retry');
            return;
        }
        const debounceTime = delaySession.getDebounceTime();
        logger.trace(`Debouncing for ${debounceTime} ms`);
        telemetry.setDebounceTime(debounceTime);
        try {
            await (0, async_2.timeout)(debounceTime, cancellationToken);
        }
        catch {
            // CancellationToken fired; return early and let the caller check isCancellationRequested
        }
    }
    determineArtificialDelayMs(delaySession, logger, telemetry) {
        if (this.simulationCtx.isInSimulationTests) {
            return;
        }
        const artificialDelay = delaySession.getArtificialDelay();
        if (artificialDelay <= 0) {
            return undefined;
        }
        logger.trace(`Enforcing artificial delay of ${artificialDelay} ms`);
        telemetry.setArtificialDelay(artificialDelay);
        return artificialDelay;
    }
    filterEdit(activeDoc, edits) {
        const allowImportChanges = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsAllowImportChanges, this.expService);
        const filters = [
            (edits) => ({ filterName: 'IgnoreImportChangesAspect', filteredEdits: importFiltering_1.IgnoreImportChangesAspect.filterEdit(activeDoc, edits, allowImportChanges) }),
            (edits) => ({ filterName: 'IgnoreEmptyLineAndLeadingTrailingWhitespaceChanges', filteredEdits: statelessNextEditProviders_1.IgnoreEmptyLineAndLeadingTrailingWhitespaceChanges.filterEdit(activeDoc, edits) }),
        ];
        if (!this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.InlineEditsAllowWhitespaceOnlyChanges, this.expService)) {
            filters.push((edits) => ({ filterName: 'IgnoreWhitespaceOnlyChanges', filteredEdits: statelessNextEditProviders_1.IgnoreWhitespaceOnlyChanges.filterEdit(activeDoc, edits) }));
        }
        const undoInsertionFiltering = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsUndoInsertionFiltering, this.expService);
        if (undoInsertionFiltering !== undefined) {
            let filter;
            switch (undoInsertionFiltering) {
                case 'v1':
                    filter = statelessNextEditProviders_1.editWouldDeleteWhatWasJustInserted;
                    break;
                case 'v2':
                    filter = statelessNextEditProviders_1.editWouldDeleteWhatWasJustInserted2;
                    break;
                default:
                    (0, assert_1.assertNever)(undoInsertionFiltering);
            }
            filters.push((edits) => ({ filterName: `UndoInsertionFiltering:${undoInsertionFiltering}`, filteredEdits: filter(activeDoc, new lineEdit_1.LineEdit(edits)) ? [] : edits }));
        }
        const substringsToFilterOut = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsFilterOutEditsWithSubstrings, this.expService);
        if (substringsToFilterOut) {
            const substrings = substringsToFilterOut
                .split(',')
                .map(s => s.trim())
                .filter(s => s.length > 0);
            filters.push((edits) => ({ filterName: 'FilterOutEditsWithSubstrings', filteredEdits: filterOutEditsWithSubstrings(edits, substrings) }));
        }
        return filters.reduce(([filteredEdits, filterNames], filter) => {
            const result = filter(filteredEdits);
            if (result.filteredEdits.length === filteredEdits.length) {
                return [filteredEdits, filterNames];
            }
            return [result.filteredEdits, [...filterNames, result.filterName]];
        }, [edits, []]);
    }
    static getNextCursorColumn(nextCursorLine) {
        return (nextCursorLine?.match(/^(\s*)/)?.at(1)?.length ?? 0) + 1;
    }
};
exports.XtabProvider = XtabProvider;
exports.XtabProvider = XtabProvider = XtabProvider_1 = __decorate([
    __param(0, inlineEditsModelService_1.IInlineEditsModelService),
    __param(1, simulationTestContext_1.ISimulationTestContext),
    __param(2, instantiation_1.IInstantiationService),
    __param(3, workspaceService_1.IWorkspaceService),
    __param(4, diffService_1.IDiffService),
    __param(5, configurationService_1.IConfigurationService),
    __param(6, nullExperimentationService_1.IExperimentationService),
    __param(7, languageContextProviderService_1.ILanguageContextProviderService),
    __param(8, languageDiagnosticsService_1.ILanguageDiagnosticsService),
    __param(9, ignoreService_1.IIgnoreService),
    __param(10, similarFilesContextService_1.ISimilarFilesContextService)
], XtabProvider);
function filterOutEditsWithSubstrings(edits, substringsToFilterOut) {
    return edits.filter(edit => {
        return edit.newLines.every(line => substringsToFilterOut.every(substring => !line.includes(substring)));
    });
}
function computeAreaAroundEditWindowLinesRange(currentDocument) {
    const cursorLine = currentDocument.cursorLineOffset;
    const areaAroundStart = Math.max(0, cursorLine - promptCrafting_1.N_LINES_AS_CONTEXT);
    const areaAroundEndExcl = Math.min(currentDocument.lines.length, cursorLine + promptCrafting_1.N_LINES_AS_CONTEXT + 1);
    return new offsetRange_1.OffsetRange(areaAroundStart, areaAroundEndExcl);
}
function mapChatFetcherErrorToNoNextEditReason(fetchError) {
    switch (fetchError.type) {
        case commonTypes_1.ChatFetchResponseType.Canceled:
            return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterFetchCall');
        case commonTypes_1.ChatFetchResponseType.OffTopic:
        case commonTypes_1.ChatFetchResponseType.Filtered:
        case commonTypes_1.ChatFetchResponseType.PromptFiltered:
        case commonTypes_1.ChatFetchResponseType.Length:
        case commonTypes_1.ChatFetchResponseType.RateLimited:
        case commonTypes_1.ChatFetchResponseType.QuotaExceeded:
        case commonTypes_1.ChatFetchResponseType.ExtensionBlocked:
        case commonTypes_1.ChatFetchResponseType.AgentUnauthorized:
        case commonTypes_1.ChatFetchResponseType.AgentFailedDependency:
        case commonTypes_1.ChatFetchResponseType.InvalidStatefulMarker:
            return new statelessNextEditProvider_1.NoNextEditReason.Uncategorized(errors_1.ErrorUtils.fromUnknown(fetchError));
        case commonTypes_1.ChatFetchResponseType.BadRequest:
        case commonTypes_1.ChatFetchResponseType.NotFound:
        case commonTypes_1.ChatFetchResponseType.Failed:
        case commonTypes_1.ChatFetchResponseType.NetworkError:
        case commonTypes_1.ChatFetchResponseType.Unknown:
            return new statelessNextEditProvider_1.NoNextEditReason.FetchFailure(errors_1.ErrorUtils.fromUnknown(fetchError));
    }
}
function overrideModelConfig(modelConfig, overridingConfig) {
    return {
        ...modelConfig,
        modelName: overridingConfig.modelName,
        promptingStrategy: overridingConfig.promptingStrategy,
        includePostScript: overridingConfig.includePostScript ?? modelConfig.includePostScript,
        currentFile: {
            ...modelConfig.currentFile,
            ...overridingConfig.currentFile,
            includeTags: overridingConfig.includeTagsInCurrentFile,
        },
        recentlyViewedDocuments: { ...modelConfig.recentlyViewedDocuments, ...overridingConfig.recentlyViewedDocuments },
        lintOptions: overridingConfig.lintOptions
            ? mergeLintOptions(modelConfig.lintOptions, overridingConfig.lintOptions)
            : modelConfig.lintOptions,
    };
}
const DEFAULT_XTAB_PROVIDER_LINT_OPTIONS = {
    ...xtabPromptOptions.DEFAULT_CURSOR_PREDICTION_LINT_OPTIONS,
    maxLineDistance: 10,
};
function mergeLintOptions(base, override) {
    const resolved = base ?? DEFAULT_XTAB_PROVIDER_LINT_OPTIONS;
    return { ...resolved, ...override };
}
function pickSystemPrompt(promptingStrategy) {
    switch (promptingStrategy) {
        case xtabPromptOptions.PromptingStrategy.UnifiedModel:
            return systemMessages_1.unifiedModelSystemPrompt;
        case xtabPromptOptions.PromptingStrategy.Codexv21NesUnified:
        case xtabPromptOptions.PromptingStrategy.SimplifiedSystemPrompt:
            return systemMessages_1.simplifiedPrompt;
        case xtabPromptOptions.PromptingStrategy.PatchBased:
        case xtabPromptOptions.PromptingStrategy.PatchBased01:
        case xtabPromptOptions.PromptingStrategy.PatchBased02:
        case xtabPromptOptions.PromptingStrategy.Xtab275:
        case xtabPromptOptions.PromptingStrategy.XtabAggressiveness:
        case xtabPromptOptions.PromptingStrategy.Xtab275Aggressiveness:
        case xtabPromptOptions.PromptingStrategy.Xtab275EditIntent:
        case xtabPromptOptions.PromptingStrategy.Xtab275EditIntentShort:
            return systemMessages_1.xtab275SystemPrompt;
        case xtabPromptOptions.PromptingStrategy.Nes41Miniv3:
            return systemMessages_1.nes41Miniv3SystemPrompt;
        case xtabPromptOptions.PromptingStrategy.CopilotNesXtab:
        case undefined:
            return systemMessages_1.systemPromptTemplate;
        default:
            (0, assert_1.assertNever)(promptingStrategy);
    }
}
function determineLanguageContextOptions(languageId, { enabled, enabledLanguages, maxTokens, enableAllContextProviders, traitPosition }) {
    if (languageId in enabledLanguages) {
        return { enabled: enabledLanguages[languageId], maxTokens, traitPosition };
    }
    if (enableAllContextProviders) {
        return { enabled: true, maxTokens, traitPosition };
    }
    return { enabled, maxTokens, traitPosition };
}
function getPredictionContents(doc, editWindowLines, responseFormat) {
    if (responseFormat === xtabPromptOptions.ResponseFormat.UnifiedWithXml) {
        return ['<EDIT>', ...editWindowLines, '</EDIT>'].join('\n');
    }
    else if (responseFormat === xtabPromptOptions.ResponseFormat.EditWindowOnly) {
        return editWindowLines.join('\n');
    }
    else if (responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntent) {
        // For EditWindowWithIntent, we predict the edit intent as high (most likely case) followed by the code
        return ['<|edit_intent|>high<|/edit_intent|>', ...editWindowLines].join('\n');
    }
    else if (responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort) {
        // For EditWindowWithIntentShort, we predict 'H' (high) followed by the code
        return ['H', ...editWindowLines].join('\n');
    }
    else if (responseFormat === xtabPromptOptions.ResponseFormat.CodeBlock) {
        return ['```', ...editWindowLines, '```'].join('\n');
    }
    else if (responseFormat === xtabPromptOptions.ResponseFormat.CustomDiffPatch) {
        const workspacePath = doc.workspaceRoot?.path;
        const workspaceRelativeDocPath = (0, promptCraftingUtils_1.toUniquePath)(doc.id, workspacePath);
        return `${workspaceRelativeDocPath}:`;
    }
    else {
        (0, assert_1.assertNever)(responseFormat);
    }
}
async function* linesWithIntermediateEditDivergenceCheck(cleanedLinesStream, cursorOriginalLinesOffset, request, editWindowLineRange, editWindowLines, fetchCts, { tracer }, setLineDiverged, mode) {
    const intermediateEdit = request.intermediateUserEdit;
    if (!intermediateEdit || intermediateEdit.isEmpty()) {
        yield* cleanedLinesStream;
        return;
    }
    const transformer = request.documentBeforeEdits.getTransformer();
    // Precompute the post-edit document once to avoid O(lines * docSize) in EditWindow mode.
    const currentDoc = intermediateEdit.apply(transformer.text);
    const currentTransformer = new positionToOffset_1.PositionOffsetTransformer(currentDoc);
    const precomputed = { currentDoc, currentTransformer };
    const shouldCheckLine = (lineIdx) => {
        if (lineIdx >= editWindowLines.length) {
            return false;
        }
        switch (mode) {
            case xtabPromptOptions_1.EarlyDivergenceCancellationMode.Cursor:
                return lineIdx === cursorOriginalLinesOffset;
            case xtabPromptOptions_1.EarlyDivergenceCancellationMode.EditWindow:
                return true;
        }
    };
    let lineIdx = 0;
    for await (const line of cleanedLinesStream) {
        if (shouldCheckLine(lineIdx)) {
            const docLineIdx = editWindowLineRange.start + lineIdx;
            const currentLine = (0, cursorLineDivergence_1.getCurrentLine)(transformer, docLineIdx, intermediateEdit, precomputed);
            if (currentLine !== undefined) {
                const originalLine = editWindowLines[lineIdx];
                if (currentLine !== originalLine // user changed this line
                    && !(0, cursorLineDivergence_1.isModelLineCompatible)(originalLine, currentLine, line) // model's line isn't compatible with user's typing
                ) {
                    setLineDiverged(true);
                    tracer.trace(`Line ${lineIdx} DIVERGED (mode=${mode}): model="${line}" current="${currentLine}"`);
                    // Cancel our local fetch token so the HTTP request is
                    // aborted immediately. We own this token, so this is safe.
                    fetchCts.cancel();
                    return;
                }
            }
        }
        yield line;
        lineIdx++;
    }
}
//# sourceMappingURL=xtabProvider.js.map