diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index cff8d5aec3..3727a7094e 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -562,7 +562,14 @@ export class Task extends EventEmitter implements TaskLike { private async addToClineMessages(message: ClineMessage) { this.clineMessages.push(message) const provider = this.providerRef.deref() - await provider?.postStateToWebview() + // Send only the new message instead of the entire state + if (provider) { + try { + await provider.postMessageToWebview({ type: "messageCreated", clineMessage: message }) + } catch (error) { + // provider.postMessageToWebview already logs; leave as non-fatal + } + } this.emit(RooCodeEventName.Message, { action: "created", message }) await this.saveClineMessages() @@ -584,7 +591,13 @@ export class Task extends EventEmitter implements TaskLike { private async updateClineMessage(message: ClineMessage) { const provider = this.providerRef.deref() - await provider?.postMessageToWebview({ type: "messageUpdated", clineMessage: message }) + if (provider) { + try { + await provider.postMessageToWebview({ type: "messageUpdated", clineMessage: message }) + } catch (error) { + // provider.postMessageToWebview already logs; leave as non-fatal + } + } this.emit(RooCodeEventName.Message, { action: "updated", message }) const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled() @@ -1172,6 +1185,9 @@ export class Task extends EventEmitter implements TaskLike { // the task first. this.apiConversationHistory = await this.getSavedApiConversationHistory() + // Send initial state to webview (this will now handle incremental message loading) + await this.providerRef.deref()?.postStateToWebview() + const lastClineMessage = this.clineMessages .slice() .reverse() diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 04d336d957..b24c627c08 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -888,7 +888,15 @@ export class ClineProvider } public async postMessageToWebview(message: ExtensionMessage) { - await this.view?.webview.postMessage(message) + try { + await this.view?.webview.postMessage(message) + } catch (error) { + // Guard against unhandled promise rejections from webview messaging + const errMsg = + error instanceof Error ? error.message : typeof error === "string" ? error : JSON.stringify(error) + this.log(`[postMessageToWebview] failed to post message '${message.type}': ${errMsg}`) + // Non-fatal: continue without throwing to avoid crashing extension host + } } private async getHMRHtmlContent(webview: vscode.Webview): Promise { @@ -1554,7 +1562,7 @@ export class ClineProvider // Check MDM compliance and send user to account tab if not compliant if (!this.checkMdmCompliance()) { - await this.postMessageToWebview({ type: "action", action: "accountButtonClicked" }) + this.postMessageToWebview({ type: "action", action: "accountButtonClicked" }) } } diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index ebdc137432..a3f3317a47 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -66,6 +66,7 @@ export interface ExtensionMessage { | "workspaceUpdated" | "invoke" | "messageUpdated" + | "messageCreated" | "mcpServers" | "enhancedPrompt" | "commitSearchResults" diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index b0045977c3..8fe9d51e5c 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -333,6 +333,14 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setCommands(message.commands ?? []) break } + case "messageCreated": { + const clineMessage = message.clineMessage! + setState((prevState) => ({ + ...prevState, + clineMessages: [...prevState.clineMessages, clineMessage], + })) + break + } case "messageUpdated": { const clineMessage = message.clineMessage! setState((prevState) => {