Omni-chat with multi-session routing - #326698
Draft
meganrogge wants to merge 41 commits into
Draft
Conversation
Introduces a frameless, always-on-top auxiliary window that hosts the real chat input (dictation, voice mode, and the input glow) via a compact, input-only ChatWidget. Submissions are scored against the current session list by a new ISessionRouter and dispatched with three outcomes: - low confidence -> start a brand-new session - several comparable high matches -> ask the user which session - a single clear match -> dispatch straight to it ISessionRouter keeps prompt/parse/heuristic logic pure so the scoring backend (renderer language model now; CAPI utility or a local model later) can be swapped without changing the contract; it degrades to a token-overlap heuristic when no model is available. Toggle via the command "Toggle Floating Chat Input Window". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aba188ca-29fb-4099-b0f9-e460fcb20613
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a floating chat-input window that routes requests to matching agent sessions.
Changes:
- Adds model-backed and heuristic session routing.
- Adds the floating ChatWidget, disambiguation picker, and fallback routing.
- Registers the services, command, contribution, and helper tests.
Show a summary per file
| File | Description |
|---|---|
src/vs/workbench/workbench.common.main.ts |
Loads the floating-window contribution. |
src/vs/workbench/contrib/chat/test/common/sessionRouter.test.ts |
Tests routing helpers. |
src/vs/workbench/contrib/chat/common/sessionRouter.ts |
Defines routing contracts and pure helpers. |
src/vs/workbench/contrib/chat/common/chatInputWindow.ts |
Defines the window service contract. |
src/vs/workbench/contrib/chat/browser/sessionRouter/sessionRouterService.ts |
Implements model-backed routing. |
src/vs/workbench/contrib/chat/browser/chatInputWindow/chatInputWindowService.ts |
Implements the window and dispatch workflow. |
src/vs/workbench/contrib/chat/browser/chatInputWindow/chatInputWindow.contribution.ts |
Registers the toggle command. |
src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts |
Registers the router service. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 16
- Review effort level: Medium
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Fix hygiene failure: replace non-ASCII box-drawing separator comment in sessionRouter.ts with ASCII; move $(add) icon outside the localized string. - Draggable frameless window: add a -webkit-app-region drag handle strip. - Localize the window document title with a placeholder. - Reapply themed colors on onDidColorThemeChange. - Dedicated accessibility help (routing + how to close) via inChatInputWindow context key, outranking the generic Quick Chat help. - Forward and clear explicit attachments through the routing submit contract. - Dedicated scoped ChatInputWindowSide menu + Close action (no longer closes the unrelated Quick Chat surface). - Only clear the input if the editor still holds the submitted text. - Window-scoped submission CancellationTokenSource, canceled on close. - Await agent-sessions model.resolve before snapshotting candidates. - Retain at most one session reference per resource (ResourceMap dedupe). - Calibrate the offline heuristic against candidate metadata + threshold test. - Coalesce concurrent openWindow calls; guard unload cleanup by window identity. - Use dom.addDisposableListener for the beforeunload listener. - Rethrow CancellationError from the router instead of degrading to heuristic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8904e5b5-6e65-4b9c-8ec5-ac060df81978
The session router scores candidate sessions as a background classification task. Pin it to the small utility model (vendor: copilot, id: copilot-utility-small) like other internal utility features (chatGoalSummaryService, chatToolRiskAssessmentService, etc.) instead of grabbing the first available Copilot model, which could be an expensive premium model and is ordering-dependent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
…uting
Routing is no longer silent. After scoring the utterance against existing
sessions, the window resolves a single pending target (top match above the
confidence threshold, biased toward the last-used session; otherwise a new
session) and shows an inline badge naming that target with a short countdown.
When the countdown elapses the request auto-sends. The user can redirect it
("Change"), abort it ("Cancel"), or just keep typing — any edit cancels the
auto-send — so a request is never dispatched without a visible, correctable
step.
- Add ROUTE_AUTOSEND_DELAY_MS countdown + pending-send badge in the aux window.
- Remember the last routed session (workspace storage) and pre-select it.
- Generalize the target picker to list all scored candidates with a preselected
entry, reused by the badge's "Change" action.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
Collaborator
Author
|
Reworked routing from silent auto-dispatch to an advisory badge with auto-send, based on a design rethink:
Known follow-up: the "Change" quick-pick still opens on the main window rather than the aux window (per-window quick input is a larger change); the badge itself is inline in the aux window. |
… wire Quick Chat Extracts the advisory-badge session routing from ChatInputWindowService into a reusable ChatSessionRoutingController (shared CSS with generic class names) and wires Quick Chat to it behind a new `chat.omni.enabled` setting. - New `chat.omni.enabled` (experimental, default off) gates the omni experience on omni surfaces such as Quick Chat. - No-match case no longer delays: it creates and sends to a new chat immediately and the badge links to that session (via IChatWidgetService.openSession) as soon as it exists. - Confident-match case keeps the countdown + Change/Cancel/type-to-cancel flow. - The floating input window now delegates to the shared controller. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
When the router auto-sends to a matched session, show a brief inline
confirmation badge ("Sent to «session»") with an Open link, so omni
surfaces that don't render the response inline (Quick Chat, aux input
window) still confirm where the request went. Auto-dismisses after 4s.
Guarded on the current submission so a newer submit isn't overwritten.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
Give more time to redirect or cancel before the matched send fires. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
The disambiguation picker renders into the focused aux window's container; since that frameless window is only tall enough for the input, the options were clipped. Resize the window to fit the rows while the picker is open (via an onPickerVisibility host hook on the shared routing controller) and restore the height on close. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ece96f2d-c878-4a57-97c5-884542b8581e
…indow # Conflicts: # src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts
…routing - Added a toggle action for the floating chat input window in the sessions header when `chat.omni.enabled` is active. - Registered the chat input window service and integrated it with the existing chat functionality. - Enhanced session routing to utilize enriched session data (description, first/last requests, and last response) for improved matching. - Updated accessibility help to describe the floating chat input window when enabled. - Implemented clipping for long content fields in session routing to prevent overflow in prompts. - Added tests to verify the new functionality and ensure proper behavior of the routing logic with enriched content.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The content surface stacked a second bordered/rounded box around the chat input's own rounded container, producing mismatched concentric corners. Make the surface an invisible transparent host with a symmetric padding ring (which also serves as the drag region), so only the input box's own consistent rounded corners show. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uick Chat width - Move close (X) action inside the input box via MenuId.ChatExecute - Focus input so the text cursor renders on open - Hide the attach-context (+) button in the omni-chat window - Remove dead ChatInputWindowSide menu and import - Clamp model-detail hover to the viewport with scroll - Widen default window to Quick Chat width (min(innerWidth * 0.62, 600)) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The native edit-context focus tracker consulted the globally-active document via getActiveElement() instead of the DOM node's own document. In the frameless auxiliary chat-input window the edit context could hold DOM focus while the tracker latched _isFocused=false, hiding the caret. Track focus against the DOM node's owner document instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make the visible input-box chrome the drag surface for the frameless floating chat input window instead of relying on the thin transparent padding ring. The .interactive-session wrapper is now marked -webkit-app-region: drag, and the interactive interior (editor, toolbars, attachments) is carved back out as no-drag so those controls stay clickable/typable. Exposes ChatInputPart.interactiveInputElements so the window host can do this without querying the DOM. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the tracker's own focus() so focus state is refreshed synchronously, avoiding reliance on the DOM focus event which is unreliable when the host window is not active (the frameless aux chat-input window case). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopt the same approach the command center uses in the title bar: a dedicated, full-size .chat-input-window-drag-region layer sits behind the input and is marked -webkit-app-region: drag in CSS, while the interactive controls (editor, toolbars, attachments) render on top and are marked no-drag. This gives a large, reliable drag surface (the input-box chrome) instead of relying on a thin transparent ring or fragile app-region inheritance. Also center the window on the window that invoked it (the active window) rather than always the main window. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…indow # Conflicts: # src/vs/workbench/contrib/chat/browser/speechToText/chatSpeechToTextService.ts # src/vs/workbench/contrib/chat/browser/voiceClient/micCaptureService.ts # src/vs/workbench/contrib/chat/test/browser/accessibility/chatAccessibilityHelp.test.ts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
… megrogge/chat-input-window
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b11adb7d-72c1-4e64-ba1e-f798dd9bf490
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.
Fixes https://github.com/microsoft/vscode-internalbacklog/issues/8461
What
Adds a floating, frameless, always-on-top omni chat input that hosts the real compact
ChatWidget, including dictation, Voice Mode, multiline editing, and the input glow. The surface has dedicated drag and close controls, grows with typed input, and expands to show routing choices below the prompt.Toggle it with Toggle Floating Chat Input Window (
workbench.action.chat.toggleInputWindow).How session finding works
Submitting does not run the request in the omni window's scratch session. Instead,
ChatSessionRoutingControllerfinds the most relevant existing agent session:ISessionRouterscores the enriched candidates with the utility language model, falling back to the token-overlap heuristic when the model is unavailable.Visible, correctable routing
Before dispatch, the omni bar renders a ranked Send To panel below the input. It shows the selected destination, close alternatives, confidence percentages and meters, and a 10-second countdown. Routing is never silent:
After dispatch, the surface briefly confirms the destination and links to a single routed session when applicable. The last routed session is remembered at workspace scope and biases later related requests.
Voice routing and backend follow-up
The client side of Voice Mode routing is implemented here:
IVoiceSessionControllerto the actual session resource before the request is sent, so subsequent voice input and response narration follow that session rather than the omni window's scratch session.omni_route: "existing_session"oromni_route: "new_session".The voice backend must add support for the new
omni_routefield. Forexisting_session, it should acknowledge the named targeted session and read/narrate that session's response. Fornew_session, it should say "I'm creating a new session" before continuing narration from the newly created target. Until that backend support lands, routing and target pinning work, but this spoken transition is not produced.Window and input behavior
Implementation
chat/common/sessionRouter.tsdefines the pure routing contract, model prompt/parser, and heuristic fallback.chat/browser/sessionRouter/performs candidate collection, bounded transcript enrichment, ranking, selection, countdown, dispatch, and confirmation.chat/browser/chatInputWindow/owns the auxiliary surface, compact widget, drag/close chrome, and content-driven sizing.chat/browser/voiceClient/voiceSessionController.tsreceives the routed target and adds the omni route decision to voice session context.Validation
npm run valid-layers-check --silent🤖 Generated with GitHub Copilot CLI
Focus-aware voice dispatch
When the omni auxiliary window owns focus, finalized Voice Mode text is submitted through its
ChatSessionRoutingControllerrather than the panel command bridge or most-recent-session fallback. The bridge is command-based to avoid a service-construction cycle while the omniChatWidgetinitializes; panel-focused Voice Mode behavior remains unchanged.