Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Removal",
"description": "Settings: No longer migrate old CodeWhisperer settings or initialize telemetry setting from AWS Toolkit."
}
7 changes: 2 additions & 5 deletions packages/core/src/amazonq/explorer/amazonQTreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as vscode from 'vscode'
import { ResourceTreeDataProvider, TreeNode } from '../../shared/treeview/resourceTreeDataProvider'
import { AuthState, isPreviousQUser } from '../../codewhisperer/util/authUtil'
import { AuthState } from '../../codewhisperer/util/authUtil'
import { createLearnMoreNode, createInstallQNode, createDismissNode } from './amazonQChildrenNodes'
import { Commands } from '../../shared/vscode/commands2'

Expand Down Expand Up @@ -40,10 +40,7 @@ export class AmazonQNode implements TreeNode {
}

public getChildren() {
const children = [createInstallQNode(), createLearnMoreNode()]
if (!isPreviousQUser()) {
children.push(createDismissNode())
}
const children = [createInstallQNode(), createLearnMoreNode(), createDismissNode()]
return children
}

Expand Down
73 changes: 0 additions & 73 deletions packages/core/src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,6 @@ export class Auth implements AuthService, ConnectionManager {
metadata: { connectionState: 'unauthenticated' },
})

// Remove the split session logout prompt, if it exists.
if (!isAmazonQ()) {
await globals.globalState.update('aws.toolkit.separationPromptDismissed', true)
}

try {
;(await tokenProvider.getToken()) ?? (await tokenProvider.createToken())
const storedProfile = await this.store.addProfile(id, profile)
Expand Down Expand Up @@ -1136,71 +1131,3 @@ export function hasVendedIamCredentials(isC9?: boolean, isSM?: boolean) {
isSM ??= isSageMaker()
return isSM || isC9
}

type LoginCommand = 'aws.toolkit.auth.manageConnections' | 'aws.codecatalyst.manageConnections'
/**
* Temporary class that handles notifiting users who were logged out as part of
* splitting auth sessions between extensions.
*
* TODO: Remove after some time.
*/
export class SessionSeparationPrompt {
// Local variable handles per session displays, e.g. we forgot a CodeCatalyst connection AND
// an Explorer only connection. We only want to display once in this case.
// However, we don't want to set this at the global state level until a user interacts with the
// notification in case they miss it the first time.
#separationPromptDisplayed = false

/**
* Open a prompt for that last used command name (or do nothing if no command name has ever been passed),
* which is useful to redisplay the prompt after reloads in case a user misses it.
*/
public async showAnyPreviousPrompt() {
const cmd = globals.globalState.tryGet('aws.toolkit.separationPromptCommand', String)
return cmd ? await this.showForCommand(cmd as LoginCommand) : undefined
}

/**
* Displays a sign in prompt to the user if they have been logged out of the Toolkit as part of
* separating auth sessions between extensions. It will executed the passed command for sign in,
* (e.g. codecatalyst sign in vs explorer)
*/
public async showForCommand(cmd: LoginCommand) {
if (
this.#separationPromptDisplayed ||
globals.globalState.get<boolean>('aws.toolkit.separationPromptDismissed')
) {
return
}

await globals.globalState.update('aws.toolkit.separationPromptCommand', cmd)

await telemetry.toolkit_showNotification.run(async () => {
telemetry.record({ id: 'sessionSeparation' })
this.#separationPromptDisplayed = true
void vscode.window
.showWarningMessage(
'Amazon Q and AWS Toolkit no longer share connections. Please sign in again to use AWS Toolkit.',
'Sign In'
)
.then(async (resp) => {
await telemetry.toolkit_invokeAction.run(async () => {
telemetry.record({ source: 'sessionSeparationNotification' })
if (resp === 'Sign In') {
telemetry.record({ action: 'signIn' })
await vscode.commands.executeCommand(cmd)
} else {
telemetry.record({ action: 'dismiss' })
}

await globals.globalState.update('aws.toolkit.separationPromptDismissed', true)
})
})
})
}

static #instance: SessionSeparationPrompt
public static get instance() {
return (this.#instance ??= new SessionSeparationPrompt())
}
}
2 changes: 0 additions & 2 deletions packages/core/src/codecatalyst/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { DevEnvActivityStarter } from './devEnv'
import { learnMoreCommand, onboardCommand, reauth } from './explorer'
import { isInDevEnv } from '../shared/vscode/env'
import { hasScopes, scopesCodeWhispererCore, getTelemetryMetadataForConn } from '../auth/connection'
import { SessionSeparationPrompt } from '../auth/auth'
import { telemetry } from '../shared/telemetry/telemetry'
import { asStringifiedStack } from '../shared/telemetry/spans'

Expand Down Expand Up @@ -64,7 +63,6 @@ export async function activate(ctx: ExtContext): Promise<void> {
})

await authProvider.secondaryAuth.forgetConnection()
await SessionSeparationPrompt.instance.showForCommand('aws.codecatalyst.manageConnections')
})
},
{ emit: false, functionId: { name: 'activate', class: 'CodeCatalyst' } }
Expand Down
3 changes: 0 additions & 3 deletions packages/core/src/codewhisperer/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,6 @@ export async function activate(context: ExtContext): Promise<void> {
localize = nls.loadMessageBundle()
const codewhispererSettings = CodeWhispererSettings.instance

// Import old CodeWhisperer settings into Amazon Q
await CodeWhispererSettings.instance.importSettings()

// initialize AuthUtil earlier to make sure it can listen to connection change events.
const auth = AuthUtil.instance
auth.initCodeWhispererHooks()
Expand Down
24 changes: 0 additions & 24 deletions packages/core/src/codewhisperer/util/authUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,30 +501,6 @@ export class AuthUtil {
}
}

/**
* Returns true if an SSO connection with AmazonQ and CodeWhisperer scopes are found,
* even if the connection is expired.
*
* Note: This function will become irrelevant if/when the Amazon Q view tree is removed
* from the toolkit.
*/
export function isPreviousQUser() {
const auth = AuthUtil.instance

if (!auth.isConnected() || !isSsoConnection(auth.conn)) {
return false
}
const missingScopes =
(auth.isEnterpriseSsoInUse() && !hasScopes(auth.conn, amazonQScopes)) ||
!hasScopes(auth.conn, codeWhispererChatScopes)

if (missingScopes) {
return false
}

return true
}

export type FeatureAuthState = { [feature in Feature]: AuthState }
export type Feature = (typeof Features)[keyof typeof Features]
export type AuthState = (typeof AuthStates)[keyof typeof AuthStates]
Expand Down
18 changes: 1 addition & 17 deletions packages/core/src/codewhisperer/util/codewhispererSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { fromExtensionManifest, migrateSetting } from '../../shared/settings'
import { fromExtensionManifest } from '../../shared/settings'
import { ArrayConstructor } from '../../shared/utilities/typeConstructors'

const description = {
Expand All @@ -17,22 +17,6 @@ const description = {
}

export class CodeWhispererSettings extends fromExtensionManifest('amazonQ', description) {
// TODO: Remove after a few releases
public async importSettings() {
await migrateSetting(
{ key: 'aws.codeWhisperer.includeSuggestionsWithCodeReferences', type: Boolean },
{ key: 'amazonQ.showInlineCodeSuggestionsWithCodeReferences' }
)
await migrateSetting(
{ key: 'aws.codeWhisperer.importRecommendation', type: Boolean },
{ key: 'amazonQ.importRecommendationForInlineCodeSuggestions' }
)
await migrateSetting(
{ key: 'aws.codeWhisperer.shareCodeWhispererContentWithAWS', type: Boolean },
{ key: 'amazonQ.shareContentWithAWS' }
)
}

public isSuggestionsWithCodeReferencesEnabled(): boolean {
return this.get(`showInlineCodeSuggestionsWithCodeReferences`, false)
}
Expand Down
85 changes: 36 additions & 49 deletions packages/core/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ import globals from './shared/extensionGlobals'
import { Experiments, Settings, showSettingsFailedMsg } from './shared/settings'
import { isReleaseVersion } from './shared/vscode/env'
import { AuthStatus, AuthUserState, telemetry } from './shared/telemetry/telemetry'
import { Auth, SessionSeparationPrompt } from './auth/auth'
import { Auth } from './auth/auth'
import { getTelemetryMetadataForConn } from './auth/connection'
import { registerSubmitFeedback } from './feedback/vue/submitFeedback'
import { activateCommon, deactivateCommon } from './extension'
import { learnMoreAmazonQCommand, qExtensionPageCommand, dismissQTree } from './amazonq/explorer/amazonQChildrenNodes'
import { AuthUtil, codeWhispererCoreScopes, isPreviousQUser } from './codewhisperer/util/authUtil'
import { codeWhispererCoreScopes } from './codewhisperer/util/authUtil'
import { installAmazonQExtension } from './codewhisperer/commands/basicCommands'
import { isExtensionInstalled, VSCODE_EXTENSION_ID } from './shared/utilities'
import { ExtensionUse, getAuthFormIdsFromConnection, initializeCredentialsProviderManager } from './auth/utils'
Expand Down Expand Up @@ -139,16 +139,8 @@ export async function activate(context: vscode.ExtensionContext) {
conn.scopes
)
await Auth.instance.forgetConnection(conn)
await SessionSeparationPrompt.instance.showForCommand('aws.toolkit.auth.manageConnections')
}
}

// Display last prompt if connections were forgotten in prior sessions
// but the user did not interact or sign in again. Useful in case the user misses it the first time.
await SessionSeparationPrompt.instance.showAnyPreviousPrompt()

// MUST restore CW/Q auth so that we can see if this user is already a Q user.
await AuthUtil.instance.restore()
},
{ emit: false, functionId: { name: 'activate', class: 'ExtensionNodeCore' } }
)
Expand Down Expand Up @@ -272,52 +264,47 @@ export async function deactivate() {

async function handleAmazonQInstall() {
const dismissedInstall = globals.globalState.get<boolean>('aws.toolkit.amazonqInstall.dismissed')
if (isExtensionInstalled(VSCODE_EXTENSION_ID.amazonq) || dismissedInstall) {
if (dismissedInstall) {
return
}

if (isExtensionInstalled(VSCODE_EXTENSION_ID.amazonq)) {
await globals.globalState.update('aws.toolkit.amazonqInstall.dismissed', true)
return
}

await telemetry.toolkit_showNotification.run(async () => {
if (isPreviousQUser()) {
await installAmazonQExtension.execute()
telemetry.record({ id: 'amazonQStandaloneInstalled' })
void vscode.window.showInformationMessage(
"Amazon Q is now its own extension.\n\nWe've auto-installed it for you with all the same features and settings from CodeWhisperer and Amazon Q chat."
telemetry.record({ id: 'amazonQStandaloneChange' })
void vscode.window
.showInformationMessage(
'Try Amazon Q, a generative AI assistant, with chat and code suggestions.',
'Install',
'Learn More'
)
await globals.globalState.update('aws.toolkit.amazonqInstall.dismissed', true)
} else {
telemetry.record({ id: 'amazonQStandaloneChange' })
void vscode.window
.showInformationMessage(
'Amazon Q has moved to its own extension.' +
'\nInstall it to use Amazon Q, a generative AI assistant, with chat and code suggestions.',
'Install',
'Learn More'
)
.then(async (resp) => {
await telemetry.toolkit_invokeAction.run(async () => {
telemetry.record({
source: ExtensionUse.instance.isFirstUse()
? ExtStartUpSources.firstStartUp
: ExtStartUpSources.none,
})

if (resp === 'Learn More') {
// Clicking learn more will open the q extension page
telemetry.record({ action: 'learnMore' })
await qExtensionPageCommand.execute()
return
}

if (resp === 'Install') {
telemetry.record({ action: 'installAmazonQ' })
await installAmazonQExtension.execute()
} else {
telemetry.record({ action: 'dismissQNotification' })
}
await globals.globalState.update('aws.toolkit.amazonqInstall.dismissed', true)
.then(async (resp) => {
await telemetry.toolkit_invokeAction.run(async () => {
telemetry.record({
source: ExtensionUse.instance.isFirstUse()
? ExtStartUpSources.firstStartUp
: ExtStartUpSources.none,
})

if (resp === 'Learn More') {
// Clicking learn more will open the q extension page
telemetry.record({ action: 'learnMore' })
await qExtensionPageCommand.execute()
return
}

if (resp === 'Install') {
telemetry.record({ action: 'installAmazonQ' })
await installAmazonQExtension.execute()
} else {
telemetry.record({ action: 'dismissQNotification' })
}
await globals.globalState.update('aws.toolkit.amazonqInstall.dismissed', true)
})
}
})
})
}

Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/shared/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ export type globalKey =
| 'aws.toolkit.amazonq.dismissed'
| 'aws.toolkit.amazonqInstall.dismissed'
| 'aws.amazonq.workspaceIndexToggleOn'
| 'aws.toolkit.separationPromptCommand'
| 'aws.toolkit.separationPromptDismissed'
// Deprecated/legacy names. New keys should start with "aws.".
| '#sessionCreationDates' // Legacy name from `ssoAccessTokenProvider.ts`.
| 'CODECATALYST_RECONNECT'
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/shared/telemetry/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export async function activate(
productName: AWSProduct
) {
const config = new TelemetryConfig(settings)
await config.initAmazonQSetting() // TODO: Remove after a few releases.

DefaultTelemetryClient.productName = productName
globals.telemetry = await DefaultTelemetryService.create(awsContext, getComputeRegion())
Expand Down
12 changes: 1 addition & 11 deletions packages/core/src/shared/telemetry/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as vscode from 'vscode'
import { env, version } from 'vscode'
import * as os from 'os'
import { getLogger } from '../logger'
import { fromExtensionManifest, migrateSetting, Settings } from '../settings'
import { fromExtensionManifest, Settings } from '../settings'
import { memoize, once } from '../utilities/functionUtils'
import {
isInDevEnv,
Expand Down Expand Up @@ -65,16 +65,6 @@ export class TelemetryConfig {
public isEnabled(): boolean {
return (isAmazonQ() ? this.amazonQConfig : this.toolkitConfig).get(`telemetry`, true)
}

public async initAmazonQSetting() {
if (!isAmazonQ() || globals.globalState.tryGet('amazonq.telemetry.migrated', Boolean, false)) {
return
}
// aws.telemetry isn't deprecated, we are just initializing amazonQ.telemetry with its value.
// This is also why we need to check that we only try this migration once.
await migrateSetting({ key: 'aws.telemetry', type: Boolean }, { key: 'amazonQ.telemetry' })
await globals.globalState.update('amazonq.telemetry.migrated', true)
}
}

export function convertLegacy(value: unknown): boolean {
Expand Down
11 changes: 1 addition & 10 deletions packages/core/src/test/techdebt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as env from '../shared/vscode/env'
// Checks project config and dependencies, to remind us to remove old things
// when possible.
describe('tech debt', function () {
// @ts-ignore
function fixByDate(date: string, msg: string) {
const now = Date.now()
const cutoffDate = Date.parse(date)
Expand Down Expand Up @@ -38,14 +39,4 @@ describe('tech debt', function () {
// This is relevant for the use of `fs.cpSync` in the copyFiles scripts.
assert.ok(semver.lt(minNodejs, '18.0.0'), 'with node18+, we can remove the dependency on @types/node@18')
})

it('remove separate sessions login edge cases', async function () {
// src/auth/auth.ts:SessionSeparationPrompt
// forgetConnection() function and calls

// Monitor telemtry to determine removal or snooze
// toolkit_showNotification.id = sessionSeparation
// auth_modifyConnection.action = deleteProfile OR auth_modifyConnection.source contains CodeCatalyst
fixByDate('2025-06-06', 'Remove the edge case code from the commit that this test is a part of.')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Removal",
"description": "Amazon Q: No longer autoinstall Amazon Q if the user had used CodeWhisperer in old Toolkit versions."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Removal",
"description": "Auth: No longer inform users that Amazon Q and Toolkit extensions have separate auth sessions."
}
Loading