Skip to content

feat: extended protocol for conversation-based agents experience support #368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 20, 2025

Conversation

volodkevych
Copy link
Contributor

@volodkevych volodkevych commented Mar 14, 2025

Problem

We need to extend chat protocol to support agent-specific flow.

Solution

Protocol and server interface were extended to support:

  • 'aws/chat/sendChatUpdate' - for pushing async updates from server to client
  • 'aws/chat/fileClick' - for opening file or executing related file action, if provided
  • 'aws/selectWorkspaceItem' - to give possibility to prompt customer select another workspace folder in IDE if current is too big
  • 'aws/openFileDiff' - to trigger open file differences in editor

ChatOptions QuickActionCommand were extended:

  • disabled was removed - disabling quick actions within quick action tabs needs to be re-modelled. Current approach will not work as chat options from different servers are merged in Flare runtimes. No real client is using this field now.

OpenTabParams params were extended to give possibility to configure contents and state of the tab if a new tab is requested.

Other minor changes:
fileList and prompts supported in chat message responses.
placeholderText can be set.

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@volodkevych volodkevych force-pushed the agents-through-chat-protocol branch from f6d6870 to 46f881c Compare March 14, 2025 19:20
@@ -81,3 +90,9 @@ export const followUpClickNotificationType = new ProtocolNotificationType<Follow
export const openTabRequestType = new ProtocolRequestType<OpenTabParams, OpenTabResult, never, void, void>(
OPEN_TAB_REQUEST_METHOD
)
export const chatUpdateNotificationType = new ProtocolNotificationType<ChatUpdateParams, void>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this update existing chat messages in the current tab or add new ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add and update - depends on messageId provided.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok! No deletion.

@@ -122,6 +127,7 @@ export const baseRuntime = (connections: { reader: MessageReader; writer: Messag
onChatPrompt: handler => lspConnection.onRequest(chatRequestType.method, handler),
onEndChat: handler => lspConnection.onRequest(endChatRequestType.method, handler),
onQuickAction: handler => lspConnection.onRequest(quickActionRequestType.method, handler),
onQuickActionTrigger: handler => lspConnection.onNotification(quickActionNotificationType.method, handler),
Copy link
Contributor

@kmile kmile Mar 17, 2025

Choose a reason for hiding this comment

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

There is a request onQuickAction, and a notification onQuickActionTrigger. Why we need both is a bit unclear.

And the type of onQuickActionTrigger is quickActionNotification.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above.

SelectWorkspaceItemResult,
} from './lsp'

export const selectWorkspaceItemRequestType = new ProtocolRequestType<
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The intent and protocol is different. We need to get selected items back in server.

Copy link
Contributor

Choose a reason for hiding this comment

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

FYI: I think we usually run into issues when we use ProtocolRequestType and therefore usually use the AutoParameterStructuresProtocolRequestType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What kind of issues?

Copy link
Contributor

Choose a reason for hiding this comment

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

When would the client send this? What is the use case?

@@ -134,6 +140,8 @@ export const baseRuntime = (connections: { reader: MessageReader; writer: Messag
onSourceLinkClick: handler => lspConnection.onNotification(sourceLinkClickNotificationType.method, handler),
onFollowUpClicked: handler => lspConnection.onNotification(followUpClickNotificationType.method, handler),
openTab: params => lspConnection.sendRequest(openTabRequestType.method, params),
chatUpdate: params => lspConnection.sendNotification(chatUpdateNotificationType.method, params),
Copy link
Contributor

@kmile kmile Mar 17, 2025

Choose a reason for hiding this comment

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

The wording of this seems to be reversed compared to the others:

  • onLinkClick
  • openFileDiff
  • showMessage
  • openTab
  • chatUpdate

should that be updateChat? or sendChatUpdate?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Kamiel, updateChat and sendChatUpdate feel easier to understand

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed

@@ -120,14 +132,18 @@ export interface QuickActions {
quickActionsCommandGroups: QuickActionCommandGroup[]
}

export interface TabData {
placeholderText?: string
messages: ChatMessage[]
Copy link
Contributor

Choose a reason for hiding this comment

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

New messages? Replace matched messages? Replace entire tab?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is an update with new or updated messages - depends on messageId

Copy link
Contributor

@justinmk3 justinmk3 Mar 20, 2025

Choose a reason for hiding this comment

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

^ should that insight/info be a docstring comment on the field here (or somewhere)?

#387

@@ -0,0 +1,24 @@
import { URI, TextDocument } from './lsp'

export const SELECT_WORKSPACE_ITEM_REQUEST_METHOD = 'aws/selectWorkspaceItem'
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 coming from the client or the server? In what situation will this be used?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Server to client, to request customer request to select folder if workspace folder is too big.

@volodkevych volodkevych marked this pull request as ready for review March 18, 2025 10:27
@volodkevych volodkevych requested a review from a team as a code owner March 18, 2025 10:27
items: WorkspaceItem[]
}

export interface OpenFileDiffParams {
Copy link
Contributor

Choose a reason for hiding this comment

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

In this case originalFileUri is the initial version of the file, and isDeleted specificies if the original file was deleted and fileContent contains the new/updated file contents, is this understanding correct?

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

@@ -319,6 +321,9 @@ export const standalone = (props: RuntimeProps) => {
onDidDeleteFiles: params => lspConnection.workspace.onDidDeleteFiles(params),
onDidRenameFiles: params => lspConnection.workspace.onDidRenameFiles(params),
onUpdateConfiguration: lspServer.setUpdateConfigurationHandler,
selectWorkspaceItem: params =>
lspConnection.sendRequest(selectWorkspaceItemRequestType.method, params),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Do we have to specify selectWorkspaceItemRequestType.method? I remember if we just passed selectWorkspaceItemRequestType it also worked and I remember when you pass the .method which is a string, you lose some benefits that come with typescript

Same applies to openFileDiff

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've seen different versions used throughout the codebase. If you know what it impacts exactly, I can change.

selectWorkspaceItem: (
handler: RequestHandler<
SelectWorkspaceItemParams,
SelectWorkspaceItemResult | undefined | null,
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this second field for partial result? Does this mean select workspace item will support partial results?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point

types/chat.ts Outdated
data?: TabData
}

export type FileAction = 'accept-change' | 'reject-change' | string
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the purpose of string here? Is it for future expansion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll remove it - whatever is in the string needs to be anyway supported by mynah-ui so better to keep it explicit.

export interface OpenTabResult extends TabEventParams {}

export interface TabState {
Copy link
Contributor

Choose a reason for hiding this comment

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

what does it mean for a tab to be in progress and cancellable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"cancellable" - practically means stop button will be shown
"inProgress" - it's being worked on by server, so no input should be accepted and progress bar shown

@volodkevych volodkevych force-pushed the agents-through-chat-protocol branch from a61df5f to e7e4a9f Compare March 19, 2025 14:34
@@ -136,6 +139,10 @@ export type Lsp = {
onDidDeleteFiles: (handler: NotificationHandler<DeleteFilesParams>) => void
onDidRenameFiles: (handler: NotificationHandler<RenameFilesParams>) => void
onUpdateConfiguration: (handler: RequestHandler<UpdateConfigurationParams, void, void>) => void
selectWorkspaceItem: (
handler: RequestHandler<SelectWorkspaceItemParams, SelectWorkspaceItemResult | undefined | null, void>
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 allow for undefined or null as a result?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Customer could click cancel in the dialog popup, something needs to be returned in this case.

Copy link
Contributor

Choose a reason for hiding this comment

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

What operations trigger this dialog popup? Is there another (existing or new) request that this relates to?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Followup action called "Choose another folder in your workspace". This is needed to request customer select a folder for context.


| Description | Method | Params | Method type | Response Type |
| ------------------------------------ | --------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| Request to select workspace item (folder, file) with the selected items returned | `aws/selectWorkspaceItem` | `SelectWorkspaceItemParams` | [Request](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage) Server to Client | `SelectWorkspaceItemResult` |
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what this means. Does this return the file contents? Show it on screen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This returns URI to selected folder/file back to LSP server. I do not think we need to return any contents as LSP server has access to contents if needed. The purpose of this method is to give customer possibility to select.

@volodkevych volodkevych merged commit 0bc496e into main Mar 20, 2025
3 checks passed
@volodkevych volodkevych deleted the agents-through-chat-protocol branch March 20, 2025 10:17

export interface TabState {
inProgress?: boolean
cancellable?: boolean
Copy link
Contributor

@justinmk3 justinmk3 Mar 20, 2025

Choose a reason for hiding this comment

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

/** Server is working, should show progress bar, should not accept input. */
inProgress?: boolean
/** Should show stop button. */
cancellable?: boolean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants