Context
Omni Chat with multi-session routing landed in #326698. The frontend chooses a destination before dispatching each request: either an existing session or a newly created session. When Voice Mode is connected, VS Code already sends that decision to the voice backend, but the backend does not yet consume it.
The Omni behavior and routing flow are described in #326698 (comment).
Existing frontend contract
Each session in the Voice Mode session context can include:
omni_route?: 'existing_session' | 'new_session';
The field is set only on the selected active session and is accompanied by the existing session identity fields:
{
"id": "<session URI>",
"label": "Auth fix",
"is_active": true,
"omni_route": "existing_session",
"agent_state": "thinking"
}
For a newly created destination, the value is new_session instead.
While Voice Mode is connected, this normally arrives through the existing merge-patch protocol:
{
"type": "session_context",
"mode": "delta",
"upserts": [
{
"id": "<session URI>",
"is_active": true,
"omni_route": "existing_session"
}
],
"removes": []
}
The same field can be present in the full session_context carried by start_session or resume_session. When Omni releases or changes the target, the delta can clear the property with "omni_route": null, following the existing JSON Merge Patch semantics.
Frontend implementation:
src/vs/workbench/contrib/chat/common/voiceClient/voiceClientService.ts defines IVoiceSessionContext.sessions[].omni_route.
src/vs/workbench/contrib/chat/browser/voiceClient/voiceSessionController.ts places the marker on the active routed target.
src/vs/workbench/contrib/chat/browser/sessionRouter/chatSessionRoutingController.ts distinguishes existing_session from new_session after a successful single-target route.
- Fan-out intentionally clears the single sticky target and does not provide an arbitrary route marker.
Backend work
- Extend the backend session-context model/parser to accept
omni_route with the two values above, including merge-patch updates and null removal.
- Treat a live transition to
existing_session as a one-shot routing event for the session identified by id. Use label when available to acknowledge the destination, for example: “Sending to Auth fix.”
- Treat a live transition to
new_session as a one-shot routing event and acknowledge that a new session is being started, for example: “Starting a new session.”
- Emit the acknowledgement promptly and before the routed session's eventual confirmation/question/response narration.
- Deduplicate by session and route transition. Repeated context deltas, agent-state changes, reconnect hydration, or unchanged values must not repeat the acknowledgement.
- A
null/missing marker clears backend route state and must not be narrated.
- Preserve all existing agent-state behavior: thinking, questions, approvals, completion summaries, interruption, and response narration should continue normally after the route acknowledgement.
- Do not announce a destination for fan-out because the frontend deliberately sends no single
omni_route target in that case.
Exact spoken wording can follow backend localization and narration conventions; the important behavior is a concise, single acknowledgement that distinguishes an existing named destination from a new session.
Acceptance criteria
Context
Omni Chat with multi-session routing landed in #326698. The frontend chooses a destination before dispatching each request: either an existing session or a newly created session. When Voice Mode is connected, VS Code already sends that decision to the voice backend, but the backend does not yet consume it.
The Omni behavior and routing flow are described in #326698 (comment).
Existing frontend contract
Each session in the Voice Mode session context can include:
The field is set only on the selected active session and is accompanied by the existing session identity fields:
{ "id": "<session URI>", "label": "Auth fix", "is_active": true, "omni_route": "existing_session", "agent_state": "thinking" }For a newly created destination, the value is
new_sessioninstead.While Voice Mode is connected, this normally arrives through the existing merge-patch protocol:
{ "type": "session_context", "mode": "delta", "upserts": [ { "id": "<session URI>", "is_active": true, "omni_route": "existing_session" } ], "removes": [] }The same field can be present in the full
session_contextcarried bystart_sessionorresume_session. When Omni releases or changes the target, the delta can clear the property with"omni_route": null, following the existing JSON Merge Patch semantics.Frontend implementation:
src/vs/workbench/contrib/chat/common/voiceClient/voiceClientService.tsdefinesIVoiceSessionContext.sessions[].omni_route.src/vs/workbench/contrib/chat/browser/voiceClient/voiceSessionController.tsplaces the marker on the active routed target.src/vs/workbench/contrib/chat/browser/sessionRouter/chatSessionRoutingController.tsdistinguishesexisting_sessionfromnew_sessionafter a successful single-target route.Backend work
omni_routewith the two values above, including merge-patch updates andnullremoval.existing_sessionas a one-shot routing event for the session identified byid. Uselabelwhen available to acknowledge the destination, for example: “Sending to Auth fix.”new_sessionas a one-shot routing event and acknowledge that a new session is being started, for example: “Starting a new session.”null/missing marker clears backend route state and must not be narrated.omni_routetarget in that case.Exact spoken wording can follow backend localization and narration conventions; the important behavior is a concise, single acknowledgement that distinguishes an existing named destination from a new session.
Acceptance criteria
session_contextupdates do not repeat the route acknowledgement.omni_routewithnullproduces no speech.