vscode to agents window handoff#316565
Draft
justschen wants to merge 1 commit into
Draft
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a “handoff” path from VS Code’s workbench chat experience to the standalone Agents window, including the ability to open the Agents window seeded with either (a) an initial query or (b) an existing contributed chat session resource.
Changes:
- Extend the native/electron “open Agents window” plumbing to optionally carry
initialQueryandsessionResourcethrough to the Agents window via IPC. - Add workbench chat entry points for handoff: a chat title-bar action (“Open in Agents Window”) plus a chat-input tip notification scoped to eligible session types.
- Update chat input notification infrastructure to support session-type scoping and dismissal events; refactor plan review UX/behavior (notably around feedback/submit flows).
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/test/electron-browser/workbenchTestServices.ts | Updates test native host service signature for the expanded openAgentsWindow options. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostPermissionUiContribution.test.ts | Adjusts a fake notification service mock to match the updated notification service interface (partially). |
| src/vs/workbench/contrib/chat/electron-browser/chat.contribution.ts | Registers new chat actions/contributions related to Agents window handoff. |
| src/vs/workbench/contrib/chat/electron-browser/agentSessions/agentSessionsActions.ts | Adds “Open session in Agents window” action and a chat-input tip contribution with per-session dismissal. |
| src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts | Passes session type provider into the notification widget and triggers rerender on session-type changes. |
| src/vs/workbench/contrib/chat/browser/widget/input/chatInputNotificationWidget.ts | Adds session-type filtering, external rerender support, and defers dismissal to a microtask. |
| src/vs/workbench/contrib/chat/browser/widget/input/chatInputNotificationService.ts | Extends notification model with sessionTypes allow-list and adds onDidDismiss. |
| src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts | Adds explanatory comment for showing an “Open in Agents Window” card (no functional change in shown hunk). |
| src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatSuggestNextWidget.ts | Adds a “Continue in Agents Window” suggested prompt button that opens Agents with an initial query. |
| src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPlanReviewPart.ts | Large plan review UX/flow refactor (feedback mode, submission handling, keyboard behavior). |
| src/vs/sessions/contrib/chat/electron-browser/chat.contribution.ts | Implements Agents window IPC handler to select folder, open an existing session, or submit an initial query. |
| src/vs/platform/windows/electron-main/windowsMainService.ts | Sends a single IPC with folder/query/sessionResource to the Agents window after opening it. |
| src/vs/platform/windows/electron-main/windows.ts | Extends IWindowsMainService.openAgentsWindow signature to carry query/session resource. |
| src/vs/platform/native/electron-main/nativeHostMainService.ts | Extends native host main service openAgentsWindow to pass query/session resource through. |
| src/vs/platform/native/common/native.ts | Extends ICommonNativeHostService.openAgentsWindow options type accordingly. |
Copilot's findings
Comments suppressed due to low confidence (1)
src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPlanReviewPart.ts:699
submitRejectionnow submits{ rejected: true }without including any overall feedback from the textarea. If the textarea is meant to collect user feedback to the agent, this looks like a regression—consider passingfeedback/feedbackOverallalong with the rejection result (or disabling the textarea when it won’t be submitted).
this._isSubmitted = true;
this._options.onSubmit({ rejected: true });
this.markUsed();
}
- Files reviewed: 15/15 changed files
- Comments generated: 9
Comment on lines
+197
to
+200
| // No plan file: keep the textarea visible from the start and | ||
| // treat as already in feedback mode. | ||
| this._isFeedbackMode = true; | ||
| this.domNode.classList.add('chat-plan-review-feedback-mode'); |
Comment on lines
687
to
690
| this._isSubmitted = true; | ||
| // Only the textarea-only flow (no planUri) attaches a draft to the action click. | ||
| const ridesAlong = !this.review.planUri; | ||
| const textareaFeedback = ridesAlong ? this._feedbackTextarea?.value.trim() : undefined; | ||
| this._options.onSubmit({ | ||
| action: action.label, | ||
| ...(action.id ? { actionId: action.id } : {}), | ||
| rejected: false, | ||
| ...(textareaFeedback ? { feedback: textareaFeedback, feedbackOverall: textareaFeedback } : {}), | ||
| }); | ||
| void this.markUsed(); | ||
| this._options.onSubmit({ action: action.label, rejected: false }); | ||
| this.markUsed(); | ||
| } |
Comment on lines
+328
to
+332
| if (ev.keyCode === KeyCode.Enter && !ev.shiftKey) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| this.submitFeedback(); | ||
| } |
Comment on lines
34
to
+40
| ipcRenderer.on('vscode:selectAgentsFolder', (_: unknown, ...args: unknown[]) => { | ||
| const folderUri = URI.revive(args[0] as UriComponents); | ||
| this.selectFolder(folderUri); | ||
| const folderUri = args[0] ? URI.revive(args[0] as UriComponents) : undefined; | ||
| const initialQuery = typeof args[1] === 'string' ? args[1] : undefined; | ||
| const sessionResource = args[2] ? URI.revive(args[2] as UriComponents) : undefined; | ||
| this.logService.info(`[AgentsHandoff] IPC received: folderUri=${folderUri?.toString() ?? '(none)'} initialQuery=${initialQuery ? 'yes' : 'no'} sessionResource=${sessionResource?.toString() ?? '(none)'}`); | ||
| this.handleOpenIntent(folderUri, initialQuery, sessionResource); | ||
| }); |
| const initialQuery = typeof args[1] === 'string' ? args[1] : undefined; | ||
| const sessionResource = args[2] ? URI.revive(args[2] as UriComponents) : undefined; | ||
| this.logService.info(`[AgentsHandoff] IPC received: folderUri=${folderUri?.toString() ?? '(none)'} initialQuery=${initialQuery ? 'yes' : 'no'} sessionResource=${sessionResource?.toString() ?? '(none)'}`); | ||
| this.handleOpenIntent(folderUri, initialQuery, sessionResource); |
Comment on lines
+146
to
+165
| private waitForActiveSession(timeoutMs = 8000): Promise<boolean> { | ||
| if (this.sessionsManagementService.activeSession.get()) { | ||
| return Promise.resolve(true); | ||
| } | ||
| return new Promise<boolean>(resolve => { | ||
| const store = new DisposableStore(); | ||
| const handle = setTimeout(() => { | ||
| store.dispose(); | ||
| resolve(!!this.sessionsManagementService.activeSession.get()); | ||
| }, timeoutMs); | ||
| store.add(autorun(reader => { | ||
| if (this.sessionsManagementService.activeSession.read(reader)) { | ||
| clearTimeout(handle); | ||
| store.dispose(); | ||
| resolve(true); | ||
| } | ||
| })); | ||
| }); | ||
| } | ||
|
|
Comment on lines
54
to
+62
| constructor( | ||
| sessionTypeProvider: (() => string | undefined) | undefined, | ||
| @IChatInputNotificationService private readonly _notificationService: IChatInputNotificationService, | ||
| @ICommandService private readonly _commandService: ICommandService, | ||
| @ITelemetryService private readonly _telemetryService: ITelemetryService, | ||
| @IMarkdownRendererService private readonly _markdownRendererService: IMarkdownRendererService, | ||
| ) { | ||
| super(); | ||
| this._sessionTypeProvider = sessionTypeProvider; |
Comment on lines
45
to
+52
| export interface IChatInputNotificationService { | ||
| readonly _serviceBrand: undefined; | ||
|
|
||
| readonly onDidChange: Event<void>; | ||
|
|
||
| /** Fires when a notification is dismissed by the user (via the X button). */ | ||
| readonly onDidDismiss: Event<string>; | ||
|
|
Comment on lines
41
to
45
| class FakeNotificationService implements IChatInputNotificationService { | ||
| declare readonly _serviceBrand: undefined; | ||
| readonly onDidChange: Event<void> = Event.None; | ||
| readonly onDidDismiss: Event<string> = Event.None; | ||
| readonly setCalls: IChatInputNotification[] = []; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Screen.Recording.2026-05-14.at.10.02.42.PM.mov