From a3d4092eef3ca2e7a875dcb4b2c232be7c0db01d Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Tue, 11 Aug 2026 19:29:23 +0500 Subject: [PATCH] nes: fix: allow external users to configure inline edit settings (#330256) copilot: fix: allow external users to configure inline edit settings Remove the team-only value restriction from selected hidden advanced inline edit settings while preserving their keys, defaults, validators, and experiment behavior. Add coverage that verifies an external user can configure all ten settings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 97f1135d-4a33-4139-ab2c-9cb8045ece70 --- .../common/configurationService.ts | 20 ++++---- .../test/configurationServiceImpl.spec.ts | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/extensions/copilot/src/platform/configuration/common/configurationService.ts b/extensions/copilot/src/platform/configuration/common/configurationService.ts index c9f62c9283b..800e9301a37 100644 --- a/extensions/copilot/src/platform/configuration/common/configurationService.ts +++ b/extensions/copilot/src/platform/configuration/common/configurationService.ts @@ -825,10 +825,10 @@ export namespace ConfigKey { * evicted — it is simply gated until the cursor returns to an * inline-renderable position. */ - export const InlineEditsNesMimicGhostTextBehavior = defineTeamInternalSetting('chat.advanced.inlineEdits.nesMimicGhostTextBehavior', ConfigType.ExperimentBased, false, vBoolean()); + export const InlineEditsNesMimicGhostTextBehavior = defineSetting('chat.advanced.inlineEdits.nesMimicGhostTextBehavior', ConfigType.ExperimentBased, false, vBoolean()); export const InlineEditsXtabProviderUsePrediction = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.usePrediction', ConfigType.ExperimentBased, true, vBoolean()); - export const InlineEditsXtabProviderPatchModelPredictionKind = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.patchModelPredictionKind', ConfigType.ExperimentBased, xtabPromptOptions.PatchModelPrediction.FilePath, xtabPromptOptions.PatchModelPrediction.VALIDATOR); - export const InlineEditsXtabProviderPatchFastYieldLineWithCursor = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.patchFastYieldLineWithCursor', ConfigType.ExperimentBased, true, vBoolean()); + export const InlineEditsXtabProviderPatchModelPredictionKind = defineSetting('chat.advanced.inlineEdits.xtabProvider.patchModelPredictionKind', ConfigType.ExperimentBased, xtabPromptOptions.PatchModelPrediction.FilePath, xtabPromptOptions.PatchModelPrediction.VALIDATOR); + export const InlineEditsXtabProviderPatchFastYieldLineWithCursor = defineSetting('chat.advanced.inlineEdits.xtabProvider.patchFastYieldLineWithCursor', ConfigType.ExperimentBased, true, vBoolean()); export const InlineEditsXtabProviderPatchFastYieldLineWithCursorMultiLine = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.patchFastYieldLineWithCursorMultiLine', ConfigType.ExperimentBased, false, vBoolean()); export const InlineEditsXtabLanguageContextEnabledLanguages = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.languageContext.enabledLanguages', ConfigType.Simple, LANGUAGE_CONTEXT_ENABLED_LANGUAGES); export const InlineEditsXtabLanguageContextTraitsPosition = defineTeamInternalSetting<'before' | 'after'>('chat.advanced.inlineEdits.xtabProvider.languageContext.traitsPosition', ConfigType.ExperimentBased, 'before'); @@ -849,15 +849,15 @@ export namespace ConfigKey { export const InlineEditsAsyncCompletions = defineTeamInternalSetting('chat.advanced.inlineEdits.asyncCompletions', ConfigType.ExperimentBased, true); export const InlineEditsDebounceUseCoreRequestTime = defineTeamInternalSetting('chat.advanced.inlineEdits.debounceUseCoreRequestTime', ConfigType.ExperimentBased, false); export const InlineEditsYieldToCopilot = defineTeamInternalSetting('chat.advanced.inlineEdits.yieldToCopilot', ConfigType.ExperimentBased, false); - export const InlineEditsExcludedProviders = defineTeamInternalSetting('chat.advanced.inlineEdits.excludedProviders', ConfigType.ExperimentBased, undefined); + export const InlineEditsExcludedProviders = defineSetting('chat.advanced.inlineEdits.excludedProviders', ConfigType.ExperimentBased, undefined); export const InlineEditsEnableGhCompletionsProvider = defineTeamInternalSetting('chat.advanced.inlineEdits.githubCompletionsProvider.enabled', ConfigType.ExperimentBased, false); export const InlineEditsCompletionsUrl = defineTeamInternalSetting('chat.advanced.inlineEdits.completionsProvider.url', ConfigType.ExperimentBased, undefined); - export const InlineEditsDebounce = defineTeamInternalSetting('chat.advanced.inlineEdits.debounce', ConfigType.ExperimentBased, 100); + export const InlineEditsDebounce = defineSetting('chat.advanced.inlineEdits.debounce', ConfigType.ExperimentBased, 100); export const InlineEditsCacheCursorDistanceCheck = defineTeamInternalSetting('chat.advanced.inlineEdits.cacheCursorDistanceCheck', ConfigType.ExperimentBased, false); - export const InlineEditsCacheDelay = defineTeamInternalSetting('chat.advanced.inlineEdits.cacheDelay', ConfigType.ExperimentBased, 200); + export const InlineEditsCacheDelay = defineSetting('chat.advanced.inlineEdits.cacheDelay', ConfigType.ExperimentBased, 200); export const InlineEditsSubsequentCacheDelay = defineTeamInternalSetting('chat.advanced.inlineEdits.subsequentCacheDelay', ConfigType.ExperimentBased, 0); export const InlineEditsSpeculativeRequestDelay = defineTeamInternalSetting('chat.advanced.inlineEdits.speculativeRequestDelay', ConfigType.ExperimentBased, 0); - export const InlineEditsRebasedCacheDelay = defineTeamInternalSetting('chat.advanced.inlineEdits.rebasedCacheDelay', ConfigType.ExperimentBased, 0); + export const InlineEditsRebasedCacheDelay = defineSetting('chat.advanced.inlineEdits.rebasedCacheDelay', ConfigType.ExperimentBased, 0); export const InlineEditsAbsorbSubsequenceTyping = defineTeamInternalSetting('chat.advanced.inlineEdits.absorbSubsequenceTyping', ConfigType.ExperimentBased, false); export const InlineEditsReverseAgreement = defineTeamInternalSetting('chat.advanced.inlineEdits.reverseAgreement', ConfigType.ExperimentBased, true); export const InlineEditsMaxImperfectAgreementLength = defineTeamInternalSetting('chat.advanced.inlineEdits.maxImperfectAgreementLength', ConfigType.ExperimentBased, 1, vNumber()); @@ -871,7 +871,7 @@ export namespace ConfigKey { */ export const InlineEditsReanchorContentOnIndentationMismatch = defineTeamInternalSetting('chat.advanced.inlineEdits.reanchorContentOnIndentationMismatch', ConfigType.ExperimentBased, false, vBoolean()); export const InlineEditsBackoffDebounceEnabled = defineTeamInternalSetting('chat.advanced.inlineEdits.backoffDebounceEnabled', ConfigType.ExperimentBased, true); - export const InlineEditsExtraDebounceEndOfLine = defineTeamInternalSetting('chat.advanced.inlineEdits.extraDebounceEndOfLine', ConfigType.ExperimentBased, 2000); + export const InlineEditsExtraDebounceEndOfLine = defineSetting('chat.advanced.inlineEdits.extraDebounceEndOfLine', ConfigType.ExperimentBased, 2000); export const InlineEditsSpeculativeRequests = defineTeamInternalSetting('chat.advanced.inlineEdits.speculativeRequests', ConfigType.ExperimentBased, SpeculativeRequestsEnablement.Off, SpeculativeRequestsEnablement.VALIDATOR); export const InlineEditsSpeculativeRequestsCursorPlacement = defineTeamInternalSetting('chat.advanced.inlineEdits.speculativeRequestsCursorPlacement', ConfigType.ExperimentBased, SpeculativeRequestsCursorPlacement.AfterEditApplied, SpeculativeRequestsCursorPlacement.VALIDATOR); export const InlineEditsSpeculativeRequestsAutoExpandEditWindowLines = defineTeamInternalSetting('chat.advanced.inlineEdits.speculativeRequestsAutoExpandEditWindowLines', ConfigType.ExperimentBased, SpeculativeRequestsAutoExpandEditWindowLines.Off, SpeculativeRequestsAutoExpandEditWindowLines.VALIDATOR); @@ -879,7 +879,7 @@ export namespace ConfigKey { export const InlineEditsDebounceOnSelectionChange = defineTeamInternalSetting('chat.advanced.inlineEdits.debounceOnSelectionChange', ConfigType.ExperimentBased, undefined); export const InlineEditsTriggerOnEditorChangeStrategy = defineTeamInternalSetting('chat.advanced.inlineEdits.triggerOnEditorChangeStrategy', ConfigType.ExperimentBased, triggerOptions.DocumentSwitchTriggerStrategy.AfterAcceptance, triggerOptions.DocumentSwitchTriggerStrategy.VALIDATOR); export const InlineEditsProviderId = defineTeamInternalSetting('chat.advanced.inlineEdits.providerId', ConfigType.ExperimentBased, undefined); - export const InlineEditsUnification = defineTeamInternalSetting('chat.advanced.inlineEdits.unification', ConfigType.ExperimentBased, false); + export const InlineEditsUnification = defineSetting('chat.advanced.inlineEdits.unification', ConfigType.ExperimentBased, false); export const InlineEditsNextCursorPredictionModelName = defineTeamInternalSetting('chat.advanced.inlineEdits.nextCursorPrediction.modelName', ConfigType.ExperimentBased, undefined); export const InlineEditsNextCursorPredictionUseEndpointProvider = defineTeamInternalSetting('chat.advanced.inlineEdits.nextCursorPrediction.useEndpointProvider', ConfigType.Simple, false, vBoolean()); export const InlineEditsNextCursorPredictionMaxResponseTokens = defineTeamInternalSetting('chat.advanced.inlineEdits.nextCursorPrediction.maxResponseTokens', ConfigType.ExperimentBased, 40); @@ -925,7 +925,7 @@ export namespace ConfigKey { export const InlineEditsXtabMaxMergeConflictLines = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.maxMergeConflictLines', ConfigType.ExperimentBased, undefined); export const InlineEditsXtabOnlyMergeConflictLines = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.onlyMergeConflictLines', ConfigType.ExperimentBased, false); export const InlineEditsXtabDuplicateAdditionsMode = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.diffPatch.duplicateAdditionsMode', ConfigType.ExperimentBased, DuplicateAdditionsMode.Off, DuplicateAdditionsMode.VALIDATOR); - export const InlineEditsXtabSplitPatchOnDiff = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.diffPatch.splitOnDiff', ConfigType.ExperimentBased, false, vBoolean()); + export const InlineEditsXtabSplitPatchOnDiff = defineSetting('chat.advanced.inlineEdits.xtabProvider.diffPatch.splitOnDiff', ConfigType.ExperimentBased, false, vBoolean()); export const InlineEditsXtabAggressivenessLevel = defineTeamInternalSetting('chat.advanced.inlineEdits.xtabProvider.aggressivenessLevel', ConfigType.ExperimentBased, xtabPromptOptions.AggressivenessLevel.Medium); export const InlineEditsAggressivenessLowMinResponseTimeMs = defineTeamInternalSetting('chat.advanced.inlineEdits.aggressiveness.lowMinResponseTimeMs', ConfigType.ExperimentBased, 1500); export const InlineEditsAggressivenessMediumMinResponseTimeMs = defineTeamInternalSetting('chat.advanced.inlineEdits.aggressiveness.mediumMinResponseTimeMs', ConfigType.ExperimentBased, 700); diff --git a/extensions/copilot/src/platform/configuration/vscode/test/configurationServiceImpl.spec.ts b/extensions/copilot/src/platform/configuration/vscode/test/configurationServiceImpl.spec.ts index 0e1526e18d6..218ed22b7b3 100644 --- a/extensions/copilot/src/platform/configuration/vscode/test/configurationServiceImpl.spec.ts +++ b/extensions/copilot/src/platform/configuration/vscode/test/configurationServiceImpl.spec.ts @@ -42,6 +42,7 @@ vi.mock('vscode', () => { }); import { ICopilotTokenStore } from '../../../authentication/common/copilotTokenStore'; +import { NullExperimentationService } from '../../../telemetry/common/nullExperimentationService'; import { ConfigKey } from '../../common/configurationService'; import { ConfigurationServiceImpl } from '../configurationServiceImpl'; @@ -73,3 +74,48 @@ describe('ConfigurationServiceImpl - migrated chat.advanced setting fallback', ( expect(value).toEqual(userValue); }); }); + +describe('ConfigurationServiceImpl - externally configurable advanced settings', () => { + test('reads advanced inline edit settings for external users', () => { + mockConfigStore.user = { + [ConfigKey.TeamInternal.InlineEditsUnification.fullyQualifiedId]: true, + [ConfigKey.TeamInternal.InlineEditsExcludedProviders.fullyQualifiedId]: 'completions,github.copilot', + [ConfigKey.TeamInternal.InlineEditsXtabProviderPatchModelPredictionKind.fullyQualifiedId]: 'currentLineCompleted', + [ConfigKey.TeamInternal.InlineEditsXtabSplitPatchOnDiff.fullyQualifiedId]: true, + [ConfigKey.TeamInternal.InlineEditsXtabProviderPatchFastYieldLineWithCursor.fullyQualifiedId]: false, + [ConfigKey.TeamInternal.InlineEditsNesMimicGhostTextBehavior.fullyQualifiedId]: true, + [ConfigKey.TeamInternal.InlineEditsRebasedCacheDelay.fullyQualifiedId]: 100, + [ConfigKey.TeamInternal.InlineEditsExtraDebounceEndOfLine.fullyQualifiedId]: 0, + [ConfigKey.TeamInternal.InlineEditsDebounce.fullyQualifiedId]: 0, + [ConfigKey.TeamInternal.InlineEditsCacheDelay.fullyQualifiedId]: 0, + }; + mockConfigStore.defaults = {}; + + const service = new ConfigurationServiceImpl(fakeTokenStore); + const experimentationService = new NullExperimentationService(); + + expect({ + unification: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsUnification, experimentationService), + excludedProviders: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsExcludedProviders, experimentationService), + patchModelPredictionKind: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchModelPredictionKind, experimentationService), + splitOnDiff: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabSplitPatchOnDiff, experimentationService), + patchFastYieldLineWithCursor: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchFastYieldLineWithCursor, experimentationService), + nesMimicGhostTextBehavior: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsNesMimicGhostTextBehavior, experimentationService), + rebasedCacheDelay: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsRebasedCacheDelay, experimentationService), + extraDebounceEndOfLine: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsExtraDebounceEndOfLine, experimentationService), + debounce: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsDebounce, experimentationService), + cacheDelay: service.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsCacheDelay, experimentationService), + }).toEqual({ + unification: true, + excludedProviders: 'completions,github.copilot', + patchModelPredictionKind: 'currentLineCompleted', + splitOnDiff: true, + patchFastYieldLineWithCursor: false, + nesMimicGhostTextBehavior: true, + rebasedCacheDelay: 100, + extraDebounceEndOfLine: 0, + debounce: 0, + cacheDelay: 0, + }); + }); +});