Skip to content

Commit a4d6b32

Browse files
committed
Removed VET.test.ts
2 parents 74011d2 + 687427c commit a4d6b32

33 files changed

+1159
-757
lines changed

P261194666.md

Lines changed: 631 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Slightly delay rendering inline completion when user is typing"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab."
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "QCodeReview tool will update CodeIssues panel along with quick action - `/review`"
4+
}

packages/amazonq/package.json

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -439,17 +439,22 @@
439439
},
440440
{
441441
"command": "aws.amazonq.openSecurityIssuePanel",
442+
"when": "false && view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
443+
"group": "inline@4"
444+
},
445+
{
446+
"command": "aws.amazonq.security.explain",
442447
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
443448
"group": "inline@4"
444449
},
445450
{
446-
"command": "aws.amazonq.security.ignore",
451+
"command": "aws.amazonq.security.generateFix",
447452
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
448453
"group": "inline@5"
449454
},
450455
{
451-
"command": "aws.amazonq.security.generateFix",
452-
"when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithoutFix",
456+
"command": "aws.amazonq.security.ignore",
457+
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
453458
"group": "inline@6"
454459
},
455460
{
@@ -532,16 +537,17 @@
532537
"aws.amazonq.submenu.securityIssueMoreActions": [
533538
{
534539
"command": "aws.amazonq.security.explain",
540+
"when": "false",
535541
"group": "1_more@1"
536542
},
537543
{
538544
"command": "aws.amazonq.applySecurityFix",
539-
"when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
545+
"when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
540546
"group": "1_more@3"
541547
},
542548
{
543549
"command": "aws.amazonq.security.regenerateFix",
544-
"when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
550+
"when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
545551
"group": "1_more@4"
546552
},
547553
{
@@ -551,21 +557,6 @@
551557
]
552558
},
553559
"commands": [
554-
{
555-
"command": "aws.amazonq.stopCmdExecution",
556-
"title": "Stop Amazon Q Command Execution",
557-
"category": "%AWS.amazonq.title%"
558-
},
559-
{
560-
"command": "aws.amazonq.runCmdExecution",
561-
"title": "Run Amazon Q Command Execution",
562-
"category": "%AWS.amazonq.title%"
563-
},
564-
{
565-
"command": "aws.amazonq.rejectCmdExecution",
566-
"title": "Reject Amazon Q Command Execution",
567-
"category": "%AWS.amazonq.title%"
568-
},
569560
{
570561
"command": "_aws.amazonq.notifications.dismiss",
571562
"title": "%AWS.generic.dismiss%",
@@ -790,6 +781,7 @@
790781
{
791782
"command": "aws.amazonq.security.explain",
792783
"title": "%AWS.command.amazonq.explainIssue%",
784+
"icon": "$(search)",
793785
"enablement": "view == aws.amazonq.SecurityIssuesTree"
794786
},
795787
{
@@ -855,24 +847,6 @@
855847
}
856848
],
857849
"keybindings": [
858-
{
859-
"command": "aws.amazonq.stopCmdExecution",
860-
"key": "ctrl+shift+backspace",
861-
"mac": "cmd+shift+backspace",
862-
"when": "aws.amazonq.amazonqChatLSP.isRunning"
863-
},
864-
{
865-
"command": "aws.amazonq.runCmdExecution",
866-
"key": "ctrl+shift+enter",
867-
"mac": "cmd+shift+enter",
868-
"when": "aws.amazonq.amazonqChatLSP.isRunning"
869-
},
870-
{
871-
"command": "aws.amazonq.rejectCmdExecution",
872-
"key": "ctrl+shift+r",
873-
"mac": "cmd+shift+r",
874-
"when": "aws.amazonq.amazonqChatLSP.isRunning"
875-
},
876850
{
877851
"command": "_aws.amazonq.focusChat.keybinding",
878852
"win": "win+alt+i",

packages/amazonq/src/app/inline/completion.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
import * as vscode from 'vscode'
5+
66
import {
77
CancellationToken,
88
InlineCompletionContext,
@@ -46,9 +46,7 @@ import { Experiments, getLogger, sleep } from 'aws-core-vscode/shared'
4646
import { debounce, messageUtils } from 'aws-core-vscode/utils'
4747
import { showEdits } from './EditRendering/imageRenderer'
4848
import { ICursorUpdateRecorder } from './cursorUpdateManager'
49-
50-
let lastDocumentDeleteEvent: vscode.TextDocumentChangeEvent | undefined = undefined
51-
let lastDocumentDeleteTime = 0
49+
import { DocumentEventListener } from './documentEventListener'
5250

5351
export class InlineCompletionManager implements Disposable {
5452
private disposable: Disposable
@@ -60,7 +58,7 @@ export class InlineCompletionManager implements Disposable {
6058

6159
private inlineTutorialAnnotation: InlineTutorialAnnotation
6260
private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults'
63-
private documentChangeListener: Disposable
61+
private documentEventListener: DocumentEventListener
6462

6563
constructor(
6664
languageClient: LanguageClient,
@@ -74,24 +72,19 @@ export class InlineCompletionManager implements Disposable {
7472
this.lineTracker = lineTracker
7573
this.recommendationService = new RecommendationService(this.sessionManager, cursorUpdateRecorder)
7674
this.inlineTutorialAnnotation = inlineTutorialAnnotation
75+
this.documentEventListener = new DocumentEventListener()
7776
this.inlineCompletionProvider = new AmazonQInlineCompletionItemProvider(
7877
languageClient,
7978
this.recommendationService,
8079
this.sessionManager,
81-
this.inlineTutorialAnnotation
80+
this.inlineTutorialAnnotation,
81+
this.documentEventListener
8282
)
8383

84-
this.documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => {
85-
if (e.contentChanges.length === 1 && e.contentChanges[0].text === '') {
86-
lastDocumentDeleteEvent = e
87-
lastDocumentDeleteTime = performance.now()
88-
}
89-
})
9084
this.disposable = languages.registerInlineCompletionItemProvider(
9185
CodeWhispererConstants.platformLanguageIds,
9286
this.inlineCompletionProvider
9387
)
94-
9588
this.lineTracker.ready()
9689
}
9790

@@ -104,8 +97,8 @@ export class InlineCompletionManager implements Disposable {
10497
this.disposable.dispose()
10598
this.lineTracker.dispose()
10699
}
107-
if (this.documentChangeListener) {
108-
this.documentChangeListener.dispose()
100+
if (this.documentEventListener) {
101+
this.documentEventListener.dispose()
109102
}
110103
}
111104

@@ -211,7 +204,8 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem
211204
private readonly languageClient: LanguageClient,
212205
private readonly recommendationService: RecommendationService,
213206
private readonly sessionManager: SessionManager,
214-
private readonly inlineTutorialAnnotation: InlineTutorialAnnotation
207+
private readonly inlineTutorialAnnotation: InlineTutorialAnnotation,
208+
private readonly documentEventListener: DocumentEventListener
215209
) {}
216210

217211
private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults'
@@ -251,8 +245,7 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem
251245
await sleep(1)
252246
// prevent user deletion invoking auto trigger
253247
// this is a best effort estimate of deletion
254-
const timeDiff = Math.abs(performance.now() - lastDocumentDeleteTime)
255-
if (timeDiff < 500 && lastDocumentDeleteEvent && lastDocumentDeleteEvent.document.uri === document.uri) {
248+
if (this.documentEventListener.isLastEventDeletion(document.uri.fsPath)) {
256249
getLogger().debug('Skip auto trigger when deleting code')
257250
return []
258251
}
@@ -393,6 +386,20 @@ ${itemLog}
393386
return []
394387
}
395388

389+
// delay the suggestion rendeing if user is actively typing
390+
// see https://github.com/aws/aws-toolkit-vscode/commit/a537602a96f498f372ed61ec9d82cf8577a9d854
391+
for (let i = 0; i < 30; i++) {
392+
const lastDocumentChange = this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath)
393+
if (
394+
lastDocumentChange &&
395+
performance.now() - lastDocumentChange.timestamp < CodeWhispererConstants.inlineSuggestionShowDelay
396+
) {
397+
await sleep(CodeWhispererConstants.showRecommendationTimerPollPeriod)
398+
} else {
399+
break
400+
}
401+
}
402+
396403
// the user typed characters from invoking suggestion cursor position to receiving suggestion position
397404
const typeahead = document.getText(new Range(position, editor.selection.active))
398405

@@ -401,7 +408,7 @@ ${itemLog}
401408
for (const item of items) {
402409
if (item.isInlineEdit) {
403410
// Check if Next Edit Prediction feature flag is enabled
404-
if (Experiments.instance.isExperimentEnabled('amazonqLSPNEP')) {
411+
if (Experiments.instance.get('amazonqLSPNEP', true)) {
405412
await showEdits(item, editor, session, this.languageClient, this)
406413
const t3 = performance.now()
407414
logstr = logstr + `- duration since trigger to NEP suggestion is displayed: ${t3 - t0}ms`
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import * as vscode from 'vscode'
6+
7+
export interface DocumentChangeEvent {
8+
event: vscode.TextDocumentChangeEvent
9+
timestamp: number
10+
}
11+
12+
export class DocumentEventListener {
13+
private lastDocumentChangeEventMap: Map<string, DocumentChangeEvent> = new Map()
14+
private documentChangeListener: vscode.Disposable
15+
private _maxDocument = 1000
16+
17+
constructor() {
18+
this.documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => {
19+
if (e.contentChanges.length > 0) {
20+
if (this.lastDocumentChangeEventMap.size > this._maxDocument) {
21+
this.lastDocumentChangeEventMap.clear()
22+
}
23+
this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: performance.now() })
24+
}
25+
})
26+
}
27+
28+
public isLastEventDeletion(filepath: string): boolean {
29+
const result = this.lastDocumentChangeEventMap.get(filepath)
30+
if (result) {
31+
const event = result.event
32+
const eventTime = result.timestamp
33+
const isDelete =
34+
(event && event.contentChanges.length === 1 && event.contentChanges[0].text === '') || false
35+
const timeDiff = Math.abs(performance.now() - eventTime)
36+
return timeDiff < 500 && isDelete
37+
}
38+
return false
39+
}
40+
41+
public getLastDocumentChangeEvent(filepath: string): DocumentChangeEvent | undefined {
42+
return this.lastDocumentChangeEventMap.get(filepath)
43+
}
44+
45+
public dispose(): void {
46+
if (this.documentChangeListener) {
47+
this.documentChangeListener.dispose()
48+
}
49+
}
50+
}

packages/amazonq/src/lsp/auth.ts

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import {
77
bearerCredentialsUpdateRequestType,
8+
iamCredentialsUpdateRequestType,
89
ConnectionMetadata,
910
NotificationType,
1011
RequestType,
@@ -17,8 +18,8 @@ import { LanguageClient } from 'vscode-languageclient'
1718
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1819
import { Writable } from 'stream'
1920
import { onceChanged } from 'aws-core-vscode/utils'
20-
import { getLogger, oneMinute } from 'aws-core-vscode/shared'
21-
import { isSsoConnection } from 'aws-core-vscode/auth'
21+
import { getLogger, oneMinute, isSageMaker } from 'aws-core-vscode/shared'
22+
import { isSsoConnection, isIamConnection } from 'aws-core-vscode/auth'
2223

2324
export const encryptionKey = crypto.randomBytes(32)
2425

@@ -78,10 +79,16 @@ export class AmazonQLspAuth {
7879
*/
7980
async refreshConnection(force: boolean = false) {
8081
const activeConnection = this.authUtil.conn
81-
if (this.authUtil.isConnectionValid() && isSsoConnection(activeConnection)) {
82-
// send the token to the language server
83-
const token = await this.authUtil.getBearerToken()
84-
await (force ? this._updateBearerToken(token) : this.updateBearerToken(token))
82+
if (this.authUtil.isConnectionValid()) {
83+
if (isSsoConnection(activeConnection)) {
84+
// Existing SSO path
85+
const token = await this.authUtil.getBearerToken()
86+
await (force ? this._updateBearerToken(token) : this.updateBearerToken(token))
87+
} else if (isSageMaker() && isIamConnection(activeConnection)) {
88+
// New SageMaker IAM path
89+
const credentials = await this.authUtil.getCredentials()
90+
await (force ? this._updateIamCredentials(credentials) : this.updateIamCredentials(credentials))
91+
}
8592
}
8693
}
8794

@@ -92,9 +99,7 @@ export class AmazonQLspAuth {
9299

93100
public updateBearerToken = onceChanged(this._updateBearerToken.bind(this))
94101
private async _updateBearerToken(token: string) {
95-
const request = await this.createUpdateCredentialsRequest({
96-
token,
97-
})
102+
const request = await this.createUpdateBearerCredentialsRequest(token)
98103

99104
// "aws/credentials/token/update"
100105
// https://github.com/aws/language-servers/blob/44d81f0b5754747d77bda60b40cc70950413a737/core/aws-lsp-core/src/credentials/credentialsProvider.ts#L27
@@ -103,15 +108,36 @@ export class AmazonQLspAuth {
103108
this.client.info(`UpdateBearerToken: ${JSON.stringify(request)}`)
104109
}
105110

111+
public updateIamCredentials = onceChanged(this._updateIamCredentials.bind(this))
112+
private async _updateIamCredentials(credentials: any) {
113+
getLogger().info(
114+
`[SageMaker Debug] Updating IAM credentials - credentials received: ${credentials ? 'YES' : 'NO'}`
115+
)
116+
if (credentials) {
117+
getLogger().info(
118+
`[SageMaker Debug] IAM credentials structure: accessKeyId=${credentials.accessKeyId ? 'present' : 'missing'}, secretAccessKey=${credentials.secretAccessKey ? 'present' : 'missing'}, sessionToken=${credentials.sessionToken ? 'present' : 'missing'}`
119+
)
120+
}
121+
122+
const request = await this.createUpdateIamCredentialsRequest(credentials)
123+
124+
// "aws/credentials/iam/update"
125+
await this.client.sendRequest(iamCredentialsUpdateRequestType.method, request)
126+
127+
this.client.info(`UpdateIamCredentials: ${JSON.stringify(request)}`)
128+
getLogger().info(`[SageMaker Debug] IAM credentials update request sent successfully`)
129+
}
130+
106131
public startTokenRefreshInterval(pollingTime: number = oneMinute / 2) {
107132
const interval = setInterval(async () => {
108133
await this.refreshConnection().catch((e) => this.logRefreshError(e))
109134
}, pollingTime)
110135
return interval
111136
}
112137

113-
private async createUpdateCredentialsRequest(data: any): Promise<UpdateCredentialsParams> {
114-
const payload = new TextEncoder().encode(JSON.stringify({ data }))
138+
private async createUpdateBearerCredentialsRequest(token: string): Promise<UpdateCredentialsParams> {
139+
const bearerCredentials = { token }
140+
const payload = new TextEncoder().encode(JSON.stringify({ data: bearerCredentials }))
115141

116142
const jwt = await new jose.CompactEncrypt(payload)
117143
.setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
@@ -127,4 +153,24 @@ export class AmazonQLspAuth {
127153
encrypted: true,
128154
}
129155
}
156+
157+
private async createUpdateIamCredentialsRequest(credentials: any): Promise<UpdateCredentialsParams> {
158+
// Extract IAM credentials structure
159+
const iamCredentials = {
160+
accessKeyId: credentials.accessKeyId,
161+
secretAccessKey: credentials.secretAccessKey,
162+
sessionToken: credentials.sessionToken,
163+
}
164+
const payload = new TextEncoder().encode(JSON.stringify({ data: iamCredentials }))
165+
166+
const jwt = await new jose.CompactEncrypt(payload)
167+
.setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
168+
.encrypt(encryptionKey)
169+
170+
return {
171+
data: jwt,
172+
// Omit metadata for IAM credentials since startUrl is undefined for non-SSO connections
173+
encrypted: true,
174+
}
175+
}
130176
}

0 commit comments

Comments
 (0)