Skip to content

Commit dffe298

Browse files
committed
Merge branch 'master' into lint/noCopyPaste2
2 parents a713f7b + 41f64ae commit dffe298

File tree

64 files changed

+2280
-552
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2280
-552
lines changed

docs/telemetry-perf.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,70 @@ How long it took from when the user stopped pressing a key to when they were sho
258258
rHandler->>User: add to already shown results
259259
end
260260
```
261+
262+
## Crash Monitoring
263+
264+
We make an attempt to gather information regarding when the IDE crashes, then report it to telemetry. This is the diagram of the steps that take place.
265+
266+
### Sequence Diagram
267+
268+
> Keep in mind that the entire sequence below is duplicated for each instance of our extension.
269+
> They all work together to "crash check" on behalf of the other crashed extension instance.
270+
271+
`Crash Service`: The high level "service" that starts the heartbeats and crash checks
272+
273+
`Heartbeat`: Sends heartbeats which signal that the extension is still running and has not crashed
274+
275+
`Crash Checker`: Observes the heartbeats, reporting a telemetry event if a crash is detected
276+
277+
`File System State`: The user's file system where we store the heartbeat files from each extension instance
278+
279+
```mermaid
280+
%%{init: {'theme':'default'}}%%
281+
sequenceDiagram
282+
autonumber
283+
284+
participant VSC as VS Code
285+
participant Service as Crash Service
286+
participant Checker as Crash Checker
287+
participant Heartbeat as Heartbeat
288+
participant State as File System State
289+
participant Telemetry as Telemetry
290+
291+
rect rgb(121, 210, 121)
292+
alt Extension Startup
293+
VSC ->> Service: activate() - Start Service
294+
295+
Service ->> Heartbeat: Start Heartbeats
296+
Heartbeat ->> State: Send Initial Heartbeat <br/> (in a folder add a unique file w/ timestamp)
297+
rect rgb(64, 191, 64)
298+
par every N minutes
299+
Heartbeat ->> State: Send Heartbeat <br/> (overwrite the unique file w/ new timestamp)
300+
end
301+
end
302+
303+
Service ->> Checker: Start Crash Checking
304+
rect rgb(64, 191, 64)
305+
par every N*2 minutes
306+
Checker ->> Checker: If computer went to sleep, skip this iteration (gives time for a heartbeat)
307+
Checker ->> State: Request all heartbeat timestamps (readdir all heartbeat files)
308+
State ->> Checker: Receive all heartbeat timestamps
309+
loop for each crashed extension (it's timestamp >= N*2 minutes)
310+
Checker ->> State: Delete heartbeat file
311+
Checker ->> Telemetry: Send metric representing a crash: session_end
312+
end
313+
end
314+
end
315+
end
316+
end
317+
318+
rect rgb(255, 128, 128)
319+
alt Graceful Shutdown
320+
VSC ->> Service: deactivate() - Stop Service
321+
Service ->> Checker: Stop
322+
Service ->> Heartbeat: Stop
323+
Heartbeat ->> State: Delete timestamp file <br/> (This is missed when a crash happens)
324+
end
325+
end
326+
327+
```
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": "Update `@workspace` index when adding or deleting a file"
4+
}
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": "fixed device code detection when running auth through tunneled vscode"
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": "Amazon Q /dev: Add stop generation action"
4+
}

packages/amazonq/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@
188188
},
189189
"views": {
190190
"amazonq": [
191+
{
192+
"id": "aws.amazonq.notifications",
193+
"name": "%AWS.notifications.title%",
194+
"when": "!isCloud9 && !aws.isSageMaker && aws.amazonq.notifications.show"
195+
},
191196
{
192197
"type": "webview",
193198
"id": "aws.amazonq.AmazonCommonAuth",
@@ -370,6 +375,13 @@
370375
"group": "cw_chat"
371376
}
372377
],
378+
"view/item/context": [
379+
{
380+
"command": "_aws.amazonq.notifications.dismiss",
381+
"when": "viewItem == amazonqNotificationStartUp",
382+
"group": "inline@1"
383+
}
384+
],
373385
"aws.amazonq.submenu.feedback": [
374386
{
375387
"command": "aws.amazonq.submitFeedback",
@@ -397,6 +409,13 @@
397409
]
398410
},
399411
"commands": [
412+
{
413+
"command": "_aws.amazonq.notifications.dismiss",
414+
"title": "%AWS.generic.dismiss%",
415+
"category": "%AWS.amazonq.title%",
416+
"enablement": "isCloud9 || !aws.isWebExtHost",
417+
"icon": "$(remove-close)"
418+
},
400419
{
401420
"command": "aws.amazonq.explainCode",
402421
"title": "%AWS.command.amazonq.explainCode%",

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ export async function activate(context: ExtensionContext) {
2525
await amazonq.TryChatCodeLensProvider.register(appInitContext.onDidChangeAmazonQVisibility.event)
2626

2727
const setupLsp = funcUtil.debounce(async () => {
28-
void amazonq.LspController.instance.trySetupLsp(context)
28+
void amazonq.LspController.instance.trySetupLsp(context, {
29+
startUrl: AuthUtil.instance.startUrl,
30+
maxIndexSize: CodeWhispererSettings.instance.getMaxIndexSize(),
31+
isVectorIndexEnabled: CodeWhispererSettings.instance.isLocalIndexEnabled(),
32+
})
2933
}, 5000)
3034

3135
context.subscriptions.push(

packages/amazonq/test/unit/amazonq/lsp/lspClient.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('Amazon Q LSP client', function () {
1818
})
1919

2020
it('encrypts payload of query ', async () => {
21-
await lspClient.query('mock_input')
21+
await lspClient.queryVectorIndex('mock_input')
2222
assert.ok(encryptFunc.calledOnce)
2323
assert.ok(encryptFunc.calledWith(JSON.stringify({ query: 'mock_input' })))
2424
const value = await encryptFunc.returnValues[0]
@@ -27,14 +27,15 @@ describe('Amazon Q LSP client', function () {
2727
})
2828

2929
it('encrypts payload of index files ', async () => {
30-
await lspClient.indexFiles(['fileA'], 'path', false)
30+
await lspClient.buildIndex(['fileA'], 'path', 'all')
3131
assert.ok(encryptFunc.calledOnce)
3232
assert.ok(
3333
encryptFunc.calledWith(
3434
JSON.stringify({
3535
filePaths: ['fileA'],
36-
rootPath: 'path',
37-
refresh: false,
36+
projectRoot: 'path',
37+
config: 'all',
38+
language: '',
3839
})
3940
)
4041
)

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"./utils": "./dist/src/shared/utilities/index.js",
2828
"./feedback": "./dist/src/feedback/index.js",
2929
"./telemetry": "./dist/src/shared/telemetry/index.js",
30-
"./dev": "./dist/src/dev/index.js"
30+
"./dev": "./dist/src/dev/index.js",
31+
"./notifications": "./dist/src/notifications/index.js"
3132
},
3233
"contributes": {
3334
"icons": {

packages/core/package.nls.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"AWS.productName.cn": "Amazon Toolkit",
66
"AWS.amazonq.productName": "Amazon Q",
77
"AWS.codecatalyst.submenu.title": "Manage CodeCatalyst",
8+
"AWS.notifications.title": "Notifications",
89
"AWS.configuration.profileDescription": "The name of the credential profile to obtain credentials from.",
910
"AWS.configuration.description.lambda.recentlyUploaded": "Recently selected Lambda upload targets.",
1011
"AWS.configuration.description.ecs.openTerminalCommand": "The command to run when starting a new interactive terminal session.",
@@ -245,6 +246,7 @@
245246
"AWS.generic.promptUpdate": "Update...",
246247
"AWS.generic.preview": "Preview",
247248
"AWS.generic.viewDocs": "View Documentation",
249+
"AWS.generic.dismiss": "Dismiss",
248250
"AWS.ssmDocument.ssm.maxItemsComputed.desc": "Controls the maximum number of problems produced by the SSM Document language server.",
249251
"AWS.walkthrough.gettingStarted.title": "Get started with AWS",
250252
"AWS.walkthrough.gettingStarted.description": "These walkthroughs help you set up the AWS Toolkit.",
@@ -299,6 +301,7 @@
299301
"AWS.amazonq.featureDev.pillText.generatingCode": "Generating code...",
300302
"AWS.amazonq.featureDev.pillText.requestingChanges": "Requesting changes ...",
301303
"AWS.amazonq.featureDev.pillText.insertCode": "Accept code",
304+
"AWS.amazonq.featureDev.pillText.stoppingCodeGeneration": "Stopping code generation...",
302305
"AWS.amazonq.featureDev.pillText.sendFeedback": "Send feedback",
303306
"AWS.amazonq.featureDev.pillText.selectFiles": "Select files for context",
304307
"AWS.amazonq.featureDev.pillText.retry": "Retry",
@@ -315,7 +318,7 @@
315318
"AWS.amazonq.featureDev.answer.sessionClosed": "Okay, I've ended this chat session. You can open a new tab to chat or start another workflow.",
316319
"AWS.amazonq.featureDev.answer.newTaskChanges": "What new task would you like to work on?",
317320
"AWS.amazonq.featureDev.placeholder.chatInputDisabled": "Chat input is disabled",
318-
"AWS.amazonq.featureDev.placeholder.additionalImprovements": "Choose an option to proceed",
321+
"AWS.amazonq.featureDev.placeholder.additionalImprovements": "Describe your task or issue in detail",
319322
"AWS.amazonq.featureDev.placeholder.feedback": "Provide feedback or comments",
320323
"AWS.amazonq.featureDev.placeholder.describe": "Describe your task or issue in detail",
321324
"AWS.amazonq.featureDev.placeholder.sessionClosed": "Open a new chat tab to continue"

packages/core/src/amazonq/lsp/lspClient.ts

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@ import * as jose from 'jose'
1717
import { Disposable, ExtensionContext } from 'vscode'
1818

1919
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'
20-
import { GetUsageRequestType, IndexRequestType, QueryRequestType, UpdateIndexRequestType, Usage } from './types'
20+
import {
21+
BuildIndexRequestPayload,
22+
BuildIndexRequestType,
23+
GetUsageRequestType,
24+
IndexConfig,
25+
QueryInlineProjectContextRequestType,
26+
QueryVectorIndexRequestType,
27+
UpdateIndexV2RequestPayload,
28+
UpdateIndexV2RequestType,
29+
Usage,
30+
} from './types'
2131
import { Writable } from 'stream'
2232
import { CodeWhispererSettings } from '../../codewhisperer/util/codewhispererSettings'
2333
import { fs, getLogger } from '../../shared'
@@ -61,52 +71,67 @@ export class LspClient {
6171
.encrypt(key)
6272
}
6373

64-
async indexFiles(request: string[], rootPath: string, refresh: boolean) {
74+
async buildIndex(paths: string[], rootPath: string, config: IndexConfig) {
75+
const payload: BuildIndexRequestPayload = {
76+
filePaths: paths,
77+
projectRoot: rootPath,
78+
config: config,
79+
language: '',
80+
}
6581
try {
66-
const encryptedRequest = await this.encrypt(
67-
JSON.stringify({
68-
filePaths: request,
69-
rootPath: rootPath,
70-
refresh: refresh,
71-
})
72-
)
73-
const resp = await this.client?.sendRequest(IndexRequestType, encryptedRequest)
82+
const encryptedRequest = await this.encrypt(JSON.stringify(payload))
83+
const resp = await this.client?.sendRequest(BuildIndexRequestType, encryptedRequest)
7484
return resp
7585
} catch (e) {
76-
getLogger().error(`LspClient: indexFiles error: ${e}`)
86+
getLogger().error(`LspClient: buildIndex error: ${e}`)
7787
return undefined
7888
}
7989
}
8090

81-
async query(request: string) {
91+
async queryVectorIndex(request: string) {
8292
try {
8393
const encryptedRequest = await this.encrypt(
8494
JSON.stringify({
8595
query: request,
8696
})
8797
)
88-
const resp = await this.client?.sendRequest(QueryRequestType, encryptedRequest)
98+
const resp = await this.client?.sendRequest(QueryVectorIndexRequestType, encryptedRequest)
8999
return resp
90100
} catch (e) {
91-
getLogger().error(`LspClient: query error: ${e}`)
101+
getLogger().error(`LspClient: queryVectorIndex error: ${e}`)
92102
return []
93103
}
94104
}
95105

106+
async queryInlineProjectContext(query: string, path: string) {
107+
try {
108+
const request = JSON.stringify({
109+
query: query,
110+
filePath: path,
111+
})
112+
const encrypted = await this.encrypt(request)
113+
const resp: any = await this.client?.sendRequest(QueryInlineProjectContextRequestType, encrypted)
114+
return resp
115+
} catch (e) {
116+
getLogger().error(`LspClient: queryInlineProjectContext error: ${e}`)
117+
throw e
118+
}
119+
}
120+
96121
async getLspServerUsage(): Promise<Usage | undefined> {
97122
if (this.client) {
98123
return (await this.client.sendRequest(GetUsageRequestType, '')) as Usage
99124
}
100125
}
101126

102-
async updateIndex(filePath: string) {
127+
async updateIndex(filePath: string[], mode: 'update' | 'remove' | 'add') {
128+
const payload: UpdateIndexV2RequestPayload = {
129+
filePaths: filePath,
130+
updateMode: mode,
131+
}
103132
try {
104-
const encryptedRequest = await this.encrypt(
105-
JSON.stringify({
106-
filePath: filePath,
107-
})
108-
)
109-
const resp = await this.client?.sendRequest(UpdateIndexRequestType, encryptedRequest)
133+
const encryptedRequest = await this.encrypt(JSON.stringify(payload))
134+
const resp = await this.client?.sendRequest(UpdateIndexV2RequestType, encryptedRequest)
110135
return resp
111136
} catch (e) {
112137
getLogger().error(`LspClient: updateIndex error: ${e}`)
@@ -197,15 +222,26 @@ export async function activate(extensionContext: ExtensionContext) {
197222
return
198223
}
199224
savedDocument = document.uri
200-
})
201-
)
202-
toDispose.push(
225+
}),
203226
vscode.window.onDidChangeActiveTextEditor((editor) => {
204227
if (savedDocument && editor && editor.document.uri.fsPath !== savedDocument.fsPath) {
205-
void LspClient.instance.updateIndex(savedDocument.fsPath)
228+
void LspClient.instance.updateIndex([savedDocument.fsPath], 'update')
206229
}
230+
}),
231+
vscode.workspace.onDidCreateFiles((e) => {
232+
void LspClient.instance.updateIndex(
233+
e.files.map((f) => f.fsPath),
234+
'add'
235+
)
236+
}),
237+
vscode.workspace.onDidDeleteFiles((e) => {
238+
void LspClient.instance.updateIndex(
239+
e.files.map((f) => f.fsPath),
240+
'remove'
241+
)
207242
})
208243
)
244+
209245
return LspClient.instance.client.onReady().then(() => {
210246
const disposableFunc = { dispose: () => rangeFormatting?.dispose() as void }
211247
toDispose.push(disposableFunc)

0 commit comments

Comments
 (0)