Adding a New Chat Message Type
The extension uses postMessage / onDidReceiveMessage for all backend↔frontend communication. Adding a new message type requires changes in 3–5 files depending on direction.
Backend → Frontend Message
When the extension backend needs to send a new event to the webview.
Step 1: Send from Backend
In the appropriate backend file, post the message via the emitter:
this.emitter.postMessage({
type: 'myNewEvent',
someData: value,
sessionId // include if session-scoped
});
Where to send from:
| Concern | File |
|---|---|
| Agent tool execution / progress | src/services/agentChatExecutor.ts |
| Session state / list updates | src/views/chatSessionController.ts |
| Settings / connection / DB ops | src/views/settingsHandler.ts |
| Chat/agent mode dispatch | src/views/chatView.ts |
Step 2: Add Type Interface (optional but recommended)
In src/webview/scripts/core/types.ts, add a typed interface:
export interface MyNewEventMessage {
type: 'myNewEvent';
someData: string;
sessionId?: string;
}
Step 3: Create the Handler
Add the handler function in the appropriate file under src/webview/scripts/core/messageHandlers/:
| Message Category | Handler File |
|---|---|
| Streaming / assistant text | streaming.ts |
| Progress groups / tool actions / errors | progress.ts |
| Approvals (tool, file edit) | approvals.ts |
| Sessions, settings, init, navigation | sessions.ts |
| New concern? | Create a new file |
// In the appropriate handler file:
export function handleMyNewEvent(msg: MyNewEventMessage) {
// Session-scoped? Guard early:
if (msg.sessionId && msg.sessionId !== currentSessionId.value) return;
// Update reactive state...
}
Step 4: Register in Router
In src/webview/scripts/core/messageHandlers/index.ts:
- •Import the handler at the top
- •Add a
casein thehandleMessageswitch:
case 'myNewEvent': handleMyNewEvent(msg as MyNewEventMessage); break;
Step 5: Persist (if needed for session history)
If this event must appear when loading a session from history, persist it as a __ui__ event in agentChatExecutor.ts before posting:
await this.persistUiEvent(sessionId, 'myNewEvent', { someData: value });
this.emitter.postMessage({ type: 'myNewEvent', someData: value, sessionId });
Then add a handler in src/webview/scripts/core/timelineBuilder.ts to reconstruct the UI from the persisted event during session load.
Frontend → Backend Message
When the webview needs to send an action to the extension backend.
Step 1: Send from Webview
Use the VS Code API stub:
vscode.postMessage({ type: 'myAction', payload: value });
Typically called from an action function in src/webview/scripts/core/actions/.
Step 2: Handle in Backend Router
In src/views/chatView.ts, add a case in the resolveWebviewView → onDidReceiveMessage switch:
case 'myAction': await this.handleMyAction(data.payload); break;
Keep the handler thin — if logic exceeds ~10 lines, delegate to the appropriate service:
- •Session operations →
chatSessionController - •Settings/connection →
settingsHandler - •Agent tool responses →
agentExecutor
Step 3: Add Action Function (if triggered by UI)
Add the action in src/webview/scripts/core/actions/ (new file or existing file by concern), then export from actions/index.ts.
Session-Scoped Messages
Most messages include a sessionId field. The webview ignores messages that don't match the active session. This enables concurrent background generation.
Backend pattern:
this.emitter.postMessage({ type: 'myEvent', data, sessionId });
Frontend guard:
if (msg.sessionId && msg.sessionId !== currentSessionId.value) return;
Checklist
Backend → Frontend:
- •
postMessage()call in the appropriate backend file - • Type interface in
src/webview/scripts/core/types.ts - • Handler function in
src/webview/scripts/core/messageHandlers/<concern>.ts - • Case in
messageHandlers/index.tsrouter switch - • Session guard (if session-scoped)
- •
persistUiEvent+timelineBuilderhandler (if must survive session reload) - • Update
ui-messages.instructions.mdprotocol table
Frontend → Backend:
- •
vscode.postMessage()call (in action function or component) - • Case in
chatView.ts→onDidReceiveMessageswitch - • Delegate to appropriate service (keep
chatView.tsthin) - • Update
ui-messages.instructions.mdprotocol table