Skip to content

Commit bef7462

Browse files
Merge master into feature/hybridChat-local
2 parents 83783bc + 457efa4 commit bef7462

File tree

32 files changed

+726
-132
lines changed

32 files changed

+726
-132
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf"
4+
}

packages/amazonq/src/app/chat/activation.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export async function activate(context: ExtensionContext) {
1818
void amazonq.LspController.instance.trySetupLsp(context, {
1919
startUrl: AuthUtil.instance.startUrl,
2020
maxIndexSize: CodeWhispererSettings.instance.getMaxIndexSize(),
21-
isVectorIndexEnabled: CodeWhispererSettings.instance.isLocalIndexEnabled(),
21+
isVectorIndexEnabled: false,
2222
})
2323
}, 5000)
2424

@@ -30,14 +30,7 @@ export async function activate(context: ExtensionContext) {
3030
amazonq.listCodeWhispererCommandsWalkthrough.register(),
3131
amazonq.focusAmazonQPanel.register(),
3232
amazonq.focusAmazonQPanelKeybinding.register(),
33-
amazonq.tryChatCodeLensCommand.register(),
34-
vscode.workspace.onDidChangeConfiguration(async (configurationChangeEvent) => {
35-
if (configurationChangeEvent.affectsConfiguration('amazonQ.workspaceIndex')) {
36-
if (CodeWhispererSettings.instance.isLocalIndexEnabled()) {
37-
void setupLsp()
38-
}
39-
}
40-
})
33+
amazonq.tryChatCodeLensCommand.register()
4134
)
4235

4336
Commands.register('aws.amazonq.learnMore', () => {

packages/amazonq/src/lsp/auth.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,29 @@ export const notificationTypes = {
6666
* Facade over our VSCode Auth that does crud operations on the language server auth
6767
*/
6868
export class AmazonQLspAuth {
69-
constructor(private readonly client: LanguageClient) {}
69+
#logErrorIfChanged = onceChanged((s) => getLogger('amazonqLsp').error(s))
70+
constructor(
71+
private readonly client: LanguageClient,
72+
private readonly authUtil: AuthUtil = AuthUtil.instance
73+
) {}
7074

7175
/**
7276
* @param force bypass memoization, and forcefully update the bearer token
7377
*/
7478
async refreshConnection(force: boolean = false) {
75-
const activeConnection = AuthUtil.instance.auth.activeConnection
76-
if (activeConnection?.type === 'sso') {
79+
const activeConnection = this.authUtil.auth.activeConnection
80+
if (activeConnection?.state === 'valid' && activeConnection?.type === 'sso') {
7781
// send the token to the language server
78-
const token = await AuthUtil.instance.getBearerToken()
82+
const token = await this.authUtil.getBearerToken()
7983
await (force ? this._updateBearerToken(token) : this.updateBearerToken(token))
8084
}
8185
}
8286

87+
async logRefreshError(e: unknown) {
88+
const err = e as Error
89+
this.#logErrorIfChanged(`Unable to update bearer token: ${err.name}:${err.message}`)
90+
}
91+
8392
public updateBearerToken = onceChanged(this._updateBearerToken.bind(this))
8493
private async _updateBearerToken(token: string) {
8594
const request = await this.createUpdateCredentialsRequest({
@@ -93,10 +102,7 @@ export class AmazonQLspAuth {
93102

94103
public startTokenRefreshInterval(pollingTime: number = oneMinute / 2) {
95104
const interval = setInterval(async () => {
96-
await this.refreshConnection().catch((e) => {
97-
getLogger('amazonqLsp').error('Unable to update bearer token: %s', (e as Error).message)
98-
clearInterval(interval)
99-
})
105+
await this.refreshConnection().catch((e) => this.logRefreshError(e))
100106
}, pollingTime)
101107
return interval
102108
}

packages/amazonq/src/lsp/chat/activation.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
8585
type: 'customization',
8686
customization: undefinedIfEmpty(getSelectedCustomization().arn),
8787
})
88+
}),
89+
globals.logOutputChannel.onDidChangeLogLevel((logLevel) => {
90+
getLogger('amazonqLsp').info(`Local log level changed to ${logLevel}, notifying LSP`)
91+
void pushConfigUpdate(languageClient, {
92+
type: 'logLevel',
93+
})
8894
})
8995
)
9096
}
@@ -98,16 +104,24 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
98104
* push the given config.
99105
*/
100106
async function pushConfigUpdate(client: LanguageClient, config: QConfigs) {
101-
if (config.type === 'profile') {
102-
await client.sendRequest(updateConfigurationRequestType.method, {
103-
section: 'aws.q',
104-
settings: { profileArn: config.profileArn },
105-
})
106-
} else if (config.type === 'customization') {
107-
client.sendNotification(DidChangeConfigurationNotification.type.method, {
108-
section: 'aws.q',
109-
settings: { customization: config.customization },
110-
})
107+
switch (config.type) {
108+
case 'profile':
109+
await client.sendRequest(updateConfigurationRequestType.method, {
110+
section: 'aws.q',
111+
settings: { profileArn: config.profileArn },
112+
})
113+
break
114+
case 'customization':
115+
client.sendNotification(DidChangeConfigurationNotification.type.method, {
116+
section: 'aws.q',
117+
settings: { customization: config.customization },
118+
})
119+
break
120+
case 'logLevel':
121+
client.sendNotification(DidChangeConfigurationNotification.type.method, {
122+
section: 'aws.logLevel',
123+
})
124+
break
111125
}
112126
}
113127
type ProfileConfig = {
@@ -118,4 +132,7 @@ type CustomizationConfig = {
118132
type: 'customization'
119133
customization: string | undefined
120134
}
121-
type QConfigs = ProfileConfig | CustomizationConfig
135+
type LogLevelConfig = {
136+
type: 'logLevel'
137+
}
138+
type QConfigs = ProfileConfig | CustomizationConfig | LogLevelConfig

packages/amazonq/src/lsp/chat/commands.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import { Commands, globals } from 'aws-core-vscode/shared'
77
import { window } from 'vscode'
88
import { AmazonQChatViewProvider } from './webviewProvider'
9+
import { CodeScanIssue } from 'aws-core-vscode/codewhisperer'
10+
import { EditorContextExtractor } from 'aws-core-vscode/codewhispererChat'
11+
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
912

1013
/**
1114
* TODO: Re-enable these once we can figure out which path they're going to live in
@@ -17,6 +20,52 @@ export function registerCommands(provider: AmazonQChatViewProvider) {
1720
registerGenericCommand('aws.amazonq.refactorCode', 'Refactor', provider),
1821
registerGenericCommand('aws.amazonq.fixCode', 'Fix', provider),
1922
registerGenericCommand('aws.amazonq.optimizeCode', 'Optimize', provider),
23+
Commands.register('aws.amazonq.generateUnitTests', async () => {
24+
DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessagePublisher().publish({
25+
sender: 'testChat',
26+
command: 'test',
27+
type: 'chatMessage',
28+
})
29+
}),
30+
Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue) => {
31+
void focusAmazonQPanel().then(async () => {
32+
const editorContextExtractor = new EditorContextExtractor()
33+
const extractedContext = await editorContextExtractor.extractContextForTrigger('ContextMenu')
34+
const selectedCode =
35+
extractedContext?.activeFileContext?.fileText
36+
?.split('\n')
37+
.slice(issue.startLine, issue.endLine)
38+
.join('\n') ?? ''
39+
40+
// The message that gets sent to the UI
41+
const uiMessage = [
42+
'Explain the ',
43+
issue.title,
44+
' issue in the following code:',
45+
'\n```\n',
46+
selectedCode,
47+
'\n```',
48+
].join('')
49+
50+
// The message that gets sent to the backend
51+
const contextMessage = `Explain the issue "${issue.title}" (${JSON.stringify(
52+
issue
53+
)}) and generate code demonstrating the fix`
54+
55+
void provider.webview?.postMessage({
56+
command: 'sendToPrompt',
57+
params: {
58+
selection: '',
59+
triggerType: 'contextMenu',
60+
prompt: {
61+
prompt: uiMessage, // what gets sent to the user
62+
escapedPrompt: contextMessage, // what gets sent to the backend
63+
},
64+
autoSubmit: true,
65+
},
66+
})
67+
})
68+
}),
2069
Commands.register('aws.amazonq.sendToPrompt', (data) => {
2170
const triggerType = getCommandTriggerType(data)
2271
const selection = getSelectedText()

packages/amazonq/src/lsp/chat/messages.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,15 @@ import * as vscode from 'vscode'
5757
import { Disposable, LanguageClient, Position, TextDocumentIdentifier } from 'vscode-languageclient'
5858
import * as jose from 'jose'
5959
import { AmazonQChatViewProvider } from './webviewProvider'
60-
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
60+
import { AuthUtil, ReferenceLogViewProvider } from 'aws-core-vscode/codewhisperer'
6161
import { amazonQDiffScheme, AmazonQPromptSettings, messages, openUrl } from 'aws-core-vscode/shared'
62-
import { DefaultAmazonQAppInitContext, messageDispatcher, EditorContentController } from 'aws-core-vscode/amazonq'
62+
import {
63+
DefaultAmazonQAppInitContext,
64+
messageDispatcher,
65+
EditorContentController,
66+
ViewDiffMessage,
67+
referenceLogText,
68+
} from 'aws-core-vscode/amazonq'
6369
import { telemetry, TelemetryBase } from 'aws-core-vscode/telemetry'
6470
import { isValidResponseError } from './error'
6571

@@ -449,17 +455,24 @@ export function registerMessageListeners(
449455
new vscode.Position(0, 0),
450456
new vscode.Position(doc.lineCount - 1, doc.lineAt(doc.lineCount - 1).text.length)
451457
)
452-
await ecc.viewDiff(
453-
{
454-
context: {
455-
activeFileContext: { filePath: params.originalFileUri },
456-
focusAreaContext: { selectionInsideExtendedCodeBlock: entireDocumentSelection },
458+
const viewDiffMessage: ViewDiffMessage = {
459+
context: {
460+
activeFileContext: {
461+
filePath: params.originalFileUri,
462+
fileText: params.originalFileContent ?? '',
463+
fileLanguage: undefined,
464+
matchPolicy: undefined,
465+
},
466+
focusAreaContext: {
467+
selectionInsideExtendedCodeBlock: entireDocumentSelection,
468+
codeBlock: '',
469+
extendedCodeBlock: '',
470+
names: undefined,
457471
},
458-
code: params.fileContent ?? '',
459472
},
460-
amazonQDiffScheme,
461-
true
462-
)
473+
code: params.fileContent ?? '',
474+
}
475+
await ecc.viewDiff(viewDiffMessage, amazonQDiffScheme)
463476
})
464477

465478
languageClient.onNotification(chatUpdateNotificationType.method, (params: ChatUpdateParams) => {
@@ -525,20 +538,25 @@ async function handlePartialResult<T extends ChatResult>(
525538
* Decodes the final chat responses from the language server before sending it to mynah UI.
526539
* Once this is called the answer response is finished
527540
*/
528-
async function handleCompleteResult<T>(
541+
async function handleCompleteResult<T extends ChatResult>(
529542
result: string | T,
530543
encryptionKey: Buffer | undefined,
531544
provider: AmazonQChatViewProvider,
532545
tabId: string,
533546
disposable: Disposable
534547
) {
535548
const decryptedMessage =
536-
typeof result === 'string' && encryptionKey ? await decodeRequest(result, encryptionKey) : result
549+
typeof result === 'string' && encryptionKey ? await decodeRequest<T>(result, encryptionKey) : (result as T)
537550
void provider.webview?.postMessage({
538551
command: chatRequestType.method,
539552
params: decryptedMessage,
540553
tabId: tabId,
541554
})
555+
556+
// only add the reference log once the request is complete, otherwise we will get duplicate log items
557+
for (const ref of decryptedMessage.codeReference ?? []) {
558+
ReferenceLogViewProvider.instance.addReferenceLog(referenceLogText(ref))
559+
}
542560
disposable.dispose()
543561
}
544562

packages/amazonq/src/lsp/chat/webviewProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider {
139139
const vscodeApi = acquireVsCodeApi()
140140
const hybridChatConnector = new HybridChatAdapter(${(await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected'},${featureConfigData},${welcomeCount},${disclaimerAcknowledged},${regionProfileString},${disabledCommands},${isSMUS},${isSM},vscodeApi.postMessage)
141141
const commands = [hybridChatConnector.initialQuickActions[0]]
142-
qChat = amazonQChat.createChat(vscodeApi, {disclaimerAcknowledged: ${disclaimerAcknowledged}, pairProgrammingAcknowledged: ${pairProgrammingAcknowledged}, quickActionCommands: commands}, hybridChatConnector, ${JSON.stringify(featureConfigData)});
142+
qChat = amazonQChat.createChat(vscodeApi, {disclaimerAcknowledged: ${disclaimerAcknowledged}, pairProgrammingAcknowledged: ${pairProgrammingAcknowledged}, agenticMode: true, quickActionCommands: commands}, hybridChatConnector, ${JSON.stringify(featureConfigData)});
143143
}
144144
window.addEventListener('message', (event) => {
145145
/**

0 commit comments

Comments
 (0)