Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ jobs:
NODE_OPTIONS: '--max-old-space-size=8192'
AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/'
AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are setting this up to increase GitHub API Limit while download the ripgrep binary.
ref: https://github.com/microsoft/vscode-ripgrep

You can produce an API key, set the GITHUB_TOKEN environment var to it, and vscode-ripgrep will use it when downloading from GitHub. This increases your API limit.
  1. Without this the most CI test will fail during download ripegrep binary.

steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down Expand Up @@ -169,6 +170,7 @@ jobs:
NODE_OPTIONS: '--max-old-space-size=8192'
AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/'
AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
1 change: 1 addition & 0 deletions buildspec/linuxTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ env:
# followed by Error: Could not delete obsolete instance handle Error: ENOENT: no such file or directory, unlink <path>
AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/'
AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/'
VSCODE_RIPGREP_TOKEN: ${GITHUB_READONLY_TOKEN}

phases:
install:
Expand Down
4 changes: 4 additions & 0 deletions buildspec/packageTestVsix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ env:
AWS_TOOLKIT_TEST_USER_DIR: '/tmp/'
# needed or else webpack will cause it to run out of memory
NODE_OPTIONS: '--max-old-space-size=8192'
# VSCODE_RIPGREP_TOKEN will be set in pre_build phase

phases:
install:
Expand All @@ -20,6 +21,9 @@ phases:
pre_build:
commands:
- export HOME=/home/codebuild-user
# Set up VSCODE_RIPGREP_TOKEN for GitHub API access
- export VSCODE_RIPGREP_TOKEN=${GITHUB_READONLY_TOKEN}
- echo "Setting up VSCODE_RIPGREP_TOKEN for GitHub API access"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar as the GITHUB TOKEN env, set it setting this up to increase GitHub API Limit while download the ripgrep binary. otherwise, the ripgrep download failed.

- bash buildspec/shared/linux-pre_build.sh

build:
Expand Down
41 changes: 35 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"dependencies": {
"@types/node": "^22.7.5",
"vscode-nls": "^5.2.0",
"vscode-nls-dev": "^4.0.4"
"vscode-nls-dev": "^4.0.4",
"@vscode/ripgrep": "1.15.11"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the right place to add it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, based on the vscode ripgrep, it asked to add in the dependencies, not dev dependencies: https://github.com/microsoft/vscode-ripgrep.

}
}
5 changes: 5 additions & 0 deletions packages/amazonq/scripts/build/copyFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ const tasks: CopyTask[] = [
target: path.join('../../node_modules', 'web-tree-sitter', 'tree-sitter.wasm'),
destination: path.join('src', 'tree-sitter.wasm'),
},
// ripgrep binary
{
target: path.join('../../node_modules', '@vscode/ripgrep', 'bin'),
destination: 'bin/',
},
]

function copy(task: CopyTask): void {
Expand Down
28 changes: 28 additions & 0 deletions packages/core/src/codewhispererChat/clients/chat/v0/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,27 @@ export class ChatSession {
private _pairProgrammingModeOn: boolean = true
private _fsWriteBackups: Map<string, FsWriteBackup> = new Map()
private _agenticLoopInProgress: boolean = false
private _searchResults: DocumentReference[] = []

// // Search-related properties
// public _lastSearchQuery?: string
// public _lastSearchPath?: string

/**
* True if messages from local history have been sent to session.
*/
localHistoryHydrated: boolean = false
private _messageIdToUpdate: string | undefined
private _messageIdToUpdateListDirectory: string | undefined
private _messageIdToUpdateGrepSearch: string | undefined

contexts: Map<string, { first: number; second: number }[]> = new Map()
// TODO: doesn't handle the edge case when two files share the same relativePath string but from different root
// e.g. root_a/file1 vs root_b/file1
relativePathToWorkspaceRoot: Map<string, string> = new Map()

// lastSearchQuery: string | undefined
// lastSearchPath: string | undefined
public get sessionIdentifier(): string {
return this.sessionId
}
Expand All @@ -71,6 +80,14 @@ export class ChatSession {
this._messageIdToUpdateListDirectory = messageId
}

public get messageIdToUpdateGrepSearch(): string | undefined {
return this._messageIdToUpdateGrepSearch
}

public setMessageIdToUpdateGrepSearch(messageId: string | undefined) {
this._messageIdToUpdateGrepSearch = messageId
}

public get agenticLoopInProgress(): boolean {
return this._agenticLoopInProgress
}
Expand Down Expand Up @@ -147,6 +164,10 @@ export class ChatSession {
public get readFiles(): DocumentReference[] {
return this._readFiles
}

public get searchResults(): DocumentReference[] {
return this._searchResults
}
public get readFolders(): DocumentReference[] {
return this._readFolders
}
Expand All @@ -159,6 +180,9 @@ export class ChatSession {
public addToReadFiles(filePath: DocumentReference) {
this._readFiles.push(filePath)
}
public setSearchResults(searchResults: DocumentReference[]) {
this.searchResults.push(...searchResults)
}
public clearListOfReadFiles() {
this._readFiles = []
}
Expand All @@ -168,6 +192,10 @@ export class ChatSession {
public clearListOfReadFolders() {
this._readFolders = []
}
public clearSearchResults() {
this._searchResults = []
this._messageIdToUpdateGrepSearch = undefined
}
async chatIam(chatRequest: SendMessageRequest): Promise<SendMessageCommandOutput> {
const client = await createQDeveloperStreamingClient()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { ConversationTracker } from '../../../storages/conversationTracker'
import { waitTimeout, Timeout } from '../../../../shared/utilities/timeoutUtils'
import { FsReadParams } from '../../../tools/fsRead'
import { ListDirectoryParams } from '../../../tools/listDirectory'
import { SanitizedRipgrepOutput } from '../../../tools/grepSearch'

export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help'

Expand Down Expand Up @@ -307,7 +308,13 @@ export class Messenger {
let explanation: string | undefined = undefined
let changeList: Change[] | undefined = undefined
let messageIdToUpdate: string | undefined = undefined
const isReadOrList: boolean = [ToolType.FsRead, ToolType.ListDirectory].includes(
// eslint-disable-next-line prettier/prettier
const isReadOrList: boolean = [
ToolType.FsRead,
ToolType.ListDirectory,
ToolType.GrepSearch,
].includes(
// eslint-disable-next-line prettier/prettier
tool.type
)
if (tool.type === ToolType.ExecuteBash) {
Expand Down Expand Up @@ -391,6 +398,12 @@ export class Messenger {
) {
session.setMessageIdToUpdateListDirectory(toolUse.toolUseId)
}
if (
session.messageIdToUpdateGrepSearch === undefined &&
tool.type === ToolType.GrepSearch
) {
session.setMessageIdToUpdateGrepSearch(toolUse.toolUseId)
}
getLogger().debug(
`SetToolUseWithError: ${toolUse.name}:${toolUse.toolUseId} with no error`
)
Expand Down Expand Up @@ -743,6 +756,80 @@ export class Messenger {
)
}

private sendGrepSearchToolMessage(
message: string,
toolUse: ToolUse,
session: ChatSession,
tabID: string,
triggerID: string,
messageIdToUpdate?: string
) {
getLogger().info(`Grep search update message is: "${message}"`)
let searchResults = session.searchResults

// Check if the message contains grep search results
if (message.includes('Found') && message.includes('matches')) {
try {
// Remove the first line summary if present
const jsonStartIndex = message.indexOf('{')
if (jsonStartIndex !== -1) {
const jsonString = message.substring(jsonStartIndex)

// Parse the JSON string to get the SanitizedRipgrepOutput
const ripgrepOutput: SanitizedRipgrepOutput = JSON.parse(jsonString)

// Convert the fileMatches to the format expected by session.searchResults
searchResults = ripgrepOutput.fileMatches.map((file) => ({
relativeFilePath: file.filePath,
// Get line numbers from the matches object
lineRanges: Object.keys(file.matches || {}).map((lineNum) => ({
first: parseInt(lineNum, 10),
second: parseInt(lineNum, 10),
})),
}))

// Store the search results in the session for context transparency
session.setSearchResults(searchResults)
}
} catch (error) {
getLogger().error(`Error parsing grep search results: ${error}`)
}
}

const contextList = session.searchResults

const itemCount = contextList.length

// Create a title based on search results
const title =
itemCount === 0 ? 'No search results found' : `Found ${itemCount} match${itemCount !== 1 ? 'es' : ''}`

// Send the tool message with the context list
this.dispatcher.sendToolMessage(
new ToolMessage(
{
message: '',
messageType: 'answer-part',
followUps: undefined,
followUpsHeader: undefined,
relatedSuggestions: undefined,
triggerID,
messageID: messageIdToUpdate ?? toolUse?.toolUseId ?? '',
userIntent: undefined,
codeBlockLanguage: undefined,
contextList,
canBeVoted: false,
buttons: undefined,
fullWidth: false,
padding: false,
codeBlockActions: undefined,
rootFolderTitle: title,
},
tabID
)
)
}

public sendPartialToolLog(
message: string,
tabID: string,
Expand All @@ -757,12 +844,18 @@ export class Messenger {
return
}

// Handle read tool and list directory messages
// Handle read tool, grep search, and list directory messages
if (
(toolUse?.name === ToolType.FsRead || toolUse?.name === ToolType.ListDirectory) &&
(toolUse?.name === ToolType.FsRead ||
toolUse?.name === ToolType.ListDirectory ||
toolUse?.name === ToolType.GrepSearch) &&
!validation.requiresAcceptance
) {
return this.sendReadAndListDirToolMessage(toolUse, session, tabID, triggerID, messageIdToUpdate)
if (toolUse?.name === ToolType.GrepSearch) {
return this.sendGrepSearchToolMessage(message, toolUse, session, tabID, triggerID, messageIdToUpdate)
} else {
return this.sendReadAndListDirToolMessage(toolUse, session, tabID, triggerID, messageIdToUpdate)
}
}

// Handle file write tool, execute bash tool and bash command output log messages
Expand Down
Loading