Skip to content

Commit 7b8903e

Browse files
dungdong-awsabhraina-awsliumofei-amazonctlai95ashishrp-aws
authored
chore: merge main (#2044)
* fix(amazonq): improve cross theme support (#2036) * fix(amazonq): fix processing empty unsupported workspace file (#2017) * test: fix integration tests on windows (#2041) * fix(amazonq): fix for mcp server permissions to prefer workspace agent config files (#2038) * chore: add integ test badge (#2042) * feat(amazonq): implement displayFindings tool (#2029) * feat(amazonq): implement displayFindings tool * feat(amazonq): add unit tests for display findings tool * fix(amazonq): simplify displayFindings unit tests, collapse switch statement * fix(amazonq): emit metrics for displayFindings, fix error handling, improve unit tests --------- Co-authored-by: abhraina-aws <[email protected]> Co-authored-by: liumofei-amazon <[email protected]> Co-authored-by: Tai Lai <[email protected]> Co-authored-by: invictus <[email protected]> Co-authored-by: BlakeLazarine <[email protected]>
1 parent ac00b94 commit 7b8903e

File tree

22 files changed

+996
-31
lines changed

22 files changed

+996
-31
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Language Servers for AWS
22

33
[![codecov](https://codecov.io/github/aws/language-servers/graph/badge.svg?token=ZSHpIVkG8S)](https://codecov.io/github/aws/language-servers)
4+
[![Integration Tests](https://github.com/aws/language-servers/actions/workflows/integration-tests.yml/badge.svg)](https://github.com/aws/language-servers/actions/workflows/integration-tests.yml)
45

56
Language servers for integration with IDEs and Editors, which implement the protocol (LSP extensions) defined in the [language-server-runtimes](https://github.com/aws/language-server-runtimes/tree/main/runtimes) repo.
67

chat-client/src/client/tabs/tabFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class TabFactory {
7777
body: `<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 200px 0 20px 0;">
7878
7979
<div style="font-size: 24px; margin-bottom: 12px;"><strong>Amazon Q</strong></div>
80-
<div style="background: rgba(255, 255, 255, 0.1); border-radius: 8px; padding: 8px; margin: 4px 0; text-align: center;">
80+
<div style="background: rgba(128, 128, 128, 0.15); border: 1px solid rgba(128, 128, 128, 0.25); border-radius: 8px; padding: 8px; margin: 4px 0; text-align: center;">
8181
<div style="font-size: 14px; margin-bottom: 4px;"><strong>Did you know?</strong></div>
8282
<div>${this.getRandomTip()}</div>
8383
</div>

integration-tests/q-agentic-chat-server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Integration tests for Q Agentic Chat Server",
55
"main": "out/index.js",
66
"scripts": {
7-
"clean": "rm -rf out/ node_modules/ tsconfig.tsbuildinfo .tsbuildinfo",
7+
"clean": "rimraf out/ node_modules/ tsconfig.tsbuildinfo .tsbuildinfo",
88
"compile": "tsc --build && cp -r src/tests/testFixture out/tests/",
99
"test-integ": "npm run compile && mocha --timeout 30000 \"./out/**/*.test.js\" --retries 2"
1010
},
@@ -22,6 +22,7 @@
2222
"jose": "^5.10.0",
2323
"json-rpc-2.0": "^1.7.1",
2424
"mocha": "^11.0.1",
25+
"rimraf": "^3.0.2",
2526
"typescript": "^5.0.0",
2627
"yauzl-promise": "^4.0.0"
2728
}

integration-tests/q-agentic-chat-server/src/tests/agenticChatInteg.test.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { JSONRPCEndpoint, LspClient } from './lspClient'
88
import { pathToFileURL } from 'url'
99
import * as crypto from 'crypto'
1010
import { EncryptionInitialization } from '@aws/lsp-core'
11-
import { authenticateServer, decryptObjectWithKey, encryptObjectWithKey } from './testUtils'
11+
import { authenticateServer, decryptObjectWithKey, encryptObjectWithKey, normalizePath } from './testUtils'
1212
import { ChatParams, ChatResult } from '@aws/language-server-runtimes/protocol'
1313
import * as fs from 'fs'
1414

@@ -172,7 +172,9 @@ describe('Q Agentic Chat Server Integration Tests', async () => {
172172
msg => msg.type === 'tool' && msg.fileList?.rootFolderTitle === '1 file read'
173173
)
174174
expect(fsReadMessage).to.exist
175-
expect(fsReadMessage?.fileList?.filePaths).to.include.members([path.join(rootPath, 'test.py')])
175+
const expectedPath = path.join(rootPath, 'test.py')
176+
const actualPaths = fsReadMessage?.fileList?.filePaths?.map(normalizePath) || []
177+
expect(actualPaths).to.include.members([normalizePath(expectedPath)])
176178
expect(fsReadMessage?.messageId?.startsWith('tooluse_')).to.be.true
177179
})
178180

@@ -192,7 +194,8 @@ describe('Q Agentic Chat Server Integration Tests', async () => {
192194
msg => msg.type === 'tool' && msg.fileList?.rootFolderTitle === '1 directory listed'
193195
)
194196
expect(listDirectoryMessage).to.exist
195-
expect(listDirectoryMessage?.fileList?.filePaths).to.include.members([rootPath])
197+
const actualPaths = listDirectoryMessage?.fileList?.filePaths?.map(normalizePath) || []
198+
expect(actualPaths).to.include.members([normalizePath(rootPath)])
196199
expect(listDirectoryMessage?.messageId?.startsWith('tooluse_')).to.be.true
197200
})
198201

@@ -216,12 +219,17 @@ describe('Q Agentic Chat Server Integration Tests', async () => {
216219
expect(executeBashMessage?.body).to.include('test.ts')
217220
})
218221

219-
it('waits for user acceptance when executing mutable bash commands', async () => {
222+
it('waits for user acceptance when executing mutable bash commands', async function () {
223+
const command =
224+
process.platform === 'win32'
225+
? 'echo %date% > timestamp.txt && echo "Timestamp saved"'
226+
: 'date > timestamp.txt && echo "Timestamp saved"'
227+
220228
const encryptedMessage = await encryptObjectWithKey<ChatParams>(
221229
{
222230
tabId,
223231
prompt: {
224-
prompt: `Run this command using the executeBash tool: \`date > timestamp.txt && echo "Timestamp saved"\``,
232+
prompt: `Run this command using the executeBash tool: \`${command}\``,
225233
},
226234
},
227235
encryptionKey
@@ -367,6 +375,7 @@ describe('Q Agentic Chat Server Integration Tests', async () => {
367375
)
368376
expect(fileSearchMessage).to.exist
369377
expect(fileSearchMessage?.messageId?.startsWith('tooluse_')).to.be.true
370-
expect(fileSearchMessage?.fileList?.filePaths).to.include.members([rootPath])
378+
const actualPaths = fileSearchMessage?.fileList?.filePaths?.map(normalizePath) || []
379+
expect(actualPaths).to.include.members([normalizePath(rootPath)])
371380
})
372381
})

integration-tests/q-agentic-chat-server/src/tests/testUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { UpdateCredentialsParams } from '@aws/language-server-runtimes/protocol'
22
import * as jose from 'jose'
3+
import * as path from 'path'
34
import { JSONRPCEndpoint } from './lspClient'
45

56
/**
@@ -72,3 +73,12 @@ async function setProfile(endpoint: JSONRPCEndpoint, profileArn: string): Promis
7273
settings: { profileArn },
7374
})
7475
}
76+
77+
/**
78+
* Normalize paths for cross-platform comparison
79+
* @param filePath - The file path to normalize
80+
* @returns Normalized path with consistent casing
81+
*/
82+
export function normalizePath(filePath: string): string {
83+
return path.resolve(filePath).toLowerCase()
84+
}

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,10 @@ import { URI } from 'vscode-uri'
201201
import { CommandCategory } from './tools/executeBash'
202202
import { UserWrittenCodeTracker } from '../../shared/userWrittenCodeTracker'
203203
import { CodeReview } from './tools/qCodeAnalysis/codeReview'
204-
import { FINDINGS_MESSAGE_SUFFIX } from './tools/qCodeAnalysis/codeReviewConstants'
204+
import {
205+
CODE_REVIEW_FINDINGS_MESSAGE_SUFFIX,
206+
DISPLAY_FINDINGS_MESSAGE_SUFFIX,
207+
} from './tools/qCodeAnalysis/codeReviewConstants'
205208
import { McpEventHandler } from './tools/mcp/mcpEventHandler'
206209
import { enabledMCP, createNamespacedToolName } from './tools/mcp/mcpUtils'
207210
import { McpManager } from './tools/mcp/mcpManager'
@@ -225,6 +228,7 @@ import { getLatestAvailableModel } from './utils/agenticChatControllerHelper'
225228
import { ActiveUserTracker } from '../../shared/activeUserTracker'
226229
import { UserContext } from '../../client/token/codewhispererbearertokenclient'
227230
import { CodeWhispererServiceToken } from '../../shared/codeWhispererService'
231+
import { DisplayFindings } from './tools/qCodeAnalysis/displayFindings'
228232

229233
type ChatHandlers = Omit<
230234
LspHandlers<Chat>,
@@ -1764,7 +1768,8 @@ export class AgenticChatController implements ChatHandlers {
17641768
break
17651769
}
17661770
case CodeReview.toolName:
1767-
// no need to write tool message for code review
1771+
case DisplayFindings.toolName:
1772+
// no need to write tool message for CodeReview or DisplayFindings
17681773
break
17691774
// — DEFAULT ⇒ Only MCP tools, but can also handle generic tool execution messages
17701775
default:
@@ -1940,11 +1945,27 @@ export class AgenticChatController implements ChatHandlers {
19401945
) {
19411946
await chatResultStream.writeResultBlock({
19421947
type: 'tool',
1943-
messageId: toolUse.toolUseId + FINDINGS_MESSAGE_SUFFIX,
1948+
messageId: toolUse.toolUseId + CODE_REVIEW_FINDINGS_MESSAGE_SUFFIX,
19441949
body: (codeReviewResult.output.content as any).findingsByFile,
19451950
})
19461951
}
19471952
break
1953+
case DisplayFindings.toolName:
1954+
// no need to write tool result for code review, this is handled by model via chat
1955+
// Push result in message so that it is picked by IDE plugin to show in issues panel
1956+
const displayFindingsResult = result as InvokeOutput
1957+
if (
1958+
displayFindingsResult?.output?.kind === 'json' &&
1959+
displayFindingsResult.output.success &&
1960+
displayFindingsResult.output.content !== undefined
1961+
) {
1962+
await chatResultStream.writeResultBlock({
1963+
type: 'tool',
1964+
messageId: toolUse.toolUseId + DISPLAY_FINDINGS_MESSAGE_SUFFIX,
1965+
body: JSON.stringify(displayFindingsResult.output.content),
1966+
})
1967+
}
1968+
break
19481969
// — DEFAULT ⇒ MCP tools
19491970
default:
19501971
await this.#handleMcpToolResult(toolUse, result, session, chatResultStream)

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpEventHandler.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,14 +1145,17 @@ export class McpEventHandler {
11451145

11461146
try {
11471147
// Skip server config check for Built-in server
1148+
const serverConfig = McpManager.instance.getAllServerConfigs().get(serverName)
11481149
if (serverName !== 'Built-in') {
1149-
const serverConfig = McpManager.instance.getAllServerConfigs().get(serverName)
11501150
if (!serverConfig) {
11511151
throw new Error(`Server '${serverName}' not found`)
11521152
}
11531153
}
11541154

1155-
const mcpServerPermission = await this.#processPermissionUpdates(updatedPermissionConfig)
1155+
const mcpServerPermission = await this.#processPermissionUpdates(
1156+
updatedPermissionConfig,
1157+
serverConfig?.__configPath__
1158+
)
11561159

11571160
// Store the permission config instead of applying it immediately
11581161
this.#pendingPermissionConfig = {
@@ -1347,10 +1350,7 @@ export class McpEventHandler {
13471350
/**
13481351
* Processes permission updates from the UI
13491352
*/
1350-
async #processPermissionUpdates(updatedPermissionConfig: any) {
1351-
// Get the appropriate agent path
1352-
const agentPath = await this.#getAgentPath()
1353-
1353+
async #processPermissionUpdates(updatedPermissionConfig: any, agentPath: string | undefined) {
13541354
const perm: MCPServerPermission = {
13551355
enabled: true,
13561356
toolPerms: {},

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/qCodeAnalysis/codeReviewConstants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ export const SKIP_DIRECTORIES = [
211211
'temp',
212212
]
213213

214-
export const FINDINGS_MESSAGE_SUFFIX = '_codeReviewFindings'
214+
export const CODE_REVIEW_FINDINGS_MESSAGE_SUFFIX = '_codeReviewFindings'
215+
export const DISPLAY_FINDINGS_MESSAGE_SUFFIX = '_displayFindings'
215216

216217
export const CODE_REVIEW_METRICS_PARENT_NAME = 'amazonq_codeReviewTool'

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/qCodeAnalysis/codeReviewUtils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,18 @@ export class CodeReviewUtils {
278278
return qCapabilities?.codeReviewInChat || false
279279
}
280280

281+
/**
282+
* Check if storing display findings in the Code Issues panel is enabled.
283+
* @param params Initialize parameters from client
284+
* @returns True if display findings is enabled, false otherwise
285+
*/
286+
public static isDisplayFindingsEnabled(params: InitializeParams | undefined): boolean {
287+
const qCapabilities = params?.initializationOptions?.aws?.awsClientCapabilities?.q as
288+
| QClientCapabilities
289+
| undefined
290+
return qCapabilities?.displayFindings || false
291+
}
292+
281293
/**
282294
* Converts a Windows absolute file path to Unix format and removes the drive letter
283295
* @param windowsPath The Windows path to convert

0 commit comments

Comments
 (0)