Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/extension/__tests__/api-send-message.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe("API - SendMessage Command", () => {
postMessageToWebview: mockPostMessageToWebview,
on: vi.fn(),
getCurrentTaskStack: vi.fn().mockReturnValue([]),
getCurrentTask: vi.fn().mockReturnValue(undefined),
viewLaunched: true,
} as unknown as ClineProvider

Expand Down
42 changes: 41 additions & 1 deletion src/extension/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as path from "path"
import * as os from "os"

import * as vscode from "vscode"
import pWaitFor from "p-wait-for"

import {
type RooCodeAPI,
Expand Down Expand Up @@ -208,9 +209,19 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
}

public async resumeTask(taskId: string): Promise<void> {
await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`)
await this.waitForWebviewLaunch(5_000)

const { historyItem } = await this.sidebarProvider.getTaskWithId(taskId)
await this.sidebarProvider.createTaskWithHistoryItem(historyItem)
await this.sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })

if (this.sidebarProvider.viewLaunched) {
await this.sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
} else {
this.log(
`[API#resumeTask] webview not launched after resume for task ${taskId}; continuing in headless mode`,
)
}
}

public async isTaskInHistory(taskId: string): Promise<boolean> {
Expand All @@ -237,6 +248,21 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
}

public async sendMessage(text?: string, images?: string[]) {
const currentTask = this.sidebarProvider.getCurrentTask()

// In headless/sandbox flows the webview may not be launched, so routing
// through invoke=sendMessage drops the message. Deliver directly to the
// task ask-response channel instead.
if (!this.sidebarProvider.viewLaunched) {
if (!currentTask) {
this.log("[API#sendMessage] no current task in headless mode; message dropped")
return
}

await currentTask.submitUserMessage(text ?? "", images)
return
}

await this.sidebarProvider.postMessageToWebview({ type: "invoke", invoke: "sendMessage", text, images })
}

Expand All @@ -252,6 +278,20 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
return this.sidebarProvider.viewLaunched
}

private async waitForWebviewLaunch(timeoutMs: number): Promise<boolean> {
try {
await pWaitFor(() => this.sidebarProvider.viewLaunched, {
timeout: timeoutMs,
interval: 50,
})

return true
} catch {
this.log(`[API#waitForWebviewLaunch] webview did not launch within ${timeoutMs}ms`)
return false
}
}

private registerListeners(provider: ClineProvider) {
provider.on(RooCodeEventName.TaskCreated, (task) => {
// Task Lifecycle
Expand Down
Loading