11import type * as vscode from "vscode"
2- import { IpcMessageType , TaskCommandName , type IpcMessage } from "@roo-code/types"
2+ import { IpcMessageType , TaskCommandName , type ClineMessage , type IpcMessage } from "@roo-code/types"
33
44import { API } from "../api"
55import type { ClineProvider } from "../../core/webview/ClineProvider"
6+ import { MessageQueueService } from "../../core/message-queue/MessageQueueService"
7+ import { Task } from "../../core/task/Task"
68
79vi . mock ( "vscode" )
810vi . mock ( "../../core/webview/ClineProvider" )
@@ -14,6 +16,33 @@ type TaskCommandHandler = (
1416
1517let taskCommandHandler : TaskCommandHandler | undefined
1618
19+ type TaskTestAccess = {
20+ addToClineMessages : ( message : ClineMessage ) => Promise < void >
21+ }
22+
23+ const createStreamingTask = ( provider : object ) => {
24+ const task = Object . create ( Task . prototype ) as Task
25+ Object . assign ( task , {
26+ abort : false ,
27+ clineMessages : [ ] ,
28+ taskId : "task-1" ,
29+ instanceId : "instance-1" ,
30+ isStreaming : true ,
31+ messageQueueService : new MessageQueueService ( ) ,
32+ providerRef : { deref : ( ) => provider } ,
33+ addToClineMessages : vi . fn ( async ( ) => { } ) ,
34+ saveClineMessages : vi . fn ( async ( ) => true ) ,
35+ updateClineMessage : vi . fn ( async ( ) => { } ) ,
36+ cancelAutoApprovalTimeout : vi . fn ( ) ,
37+ checkpointSave : vi . fn ( async ( ) => { } ) ,
38+ emit : vi . fn ( ) ,
39+ } )
40+ vi . spyOn ( task as unknown as TaskTestAccess , "addToClineMessages" ) . mockImplementation ( async ( message ) => {
41+ task . clineMessages . push ( message )
42+ } )
43+ return task
44+ }
45+
1746vi . mock ( "@roo-code/ipc" , ( ) => ( {
1847 IpcServer : class {
1948 listen ( ) { }
@@ -26,10 +55,11 @@ vi.mock("@roo-code/ipc", () => ({
2655} ) )
2756
2857describe ( "API.sendMessage" , ( ) => {
29- it ( "enqueues directly when the current task is streaming" , async ( ) => {
58+ it ( "enqueues directly when the current webview task is streaming" , async ( ) => {
3059 const addMessage = vi . fn ( )
3160 const postMessageToWebview = vi . fn ( )
3261 const provider = {
62+ viewLaunched : true ,
3363 getCurrentTask : vi . fn ( ) . mockReturnValue ( {
3464 isStreaming : true ,
3565 messageQueueService : { addMessage } ,
@@ -51,29 +81,41 @@ describe("API.sendMessage", () => {
5181 expect ( addMessage ) . toHaveBeenCalledWith ( "" , images )
5282 } )
5383
54- it ( "delivers SendMessage commands through the IPC handler" , async ( ) => {
55- const addMessage = vi . fn ( )
84+ it . each ( [
85+ [ "command" , "npm publish" ] ,
86+ [ "use_mcp_server" , '{"server_name":"filesystem","tool_name":"write_file"}' ] ,
87+ ] as const ) ( "does not approve a protected headless %s ask from queued IPC input" , async ( askType , askText ) => {
5688 const appendLine = vi . fn ( )
5789 const provider = {
5890 context : { } ,
5991 cwd : "/test/cwd" ,
60- getCurrentTask : vi . fn ( ) . mockReturnValue ( {
61- isStreaming : true ,
62- messageQueueService : { addMessage } ,
63- } ) ,
92+ viewLaunched : false ,
93+ getState : vi . fn ( ) . mockResolvedValue ( { autoApprovalEnabled : false } ) ,
94+ getCurrentTask : vi . fn ( ) ,
6495 getCurrentTaskStack : vi . fn ( ) . mockReturnValue ( [ ] ) ,
6596 on : vi . fn ( ) ,
6697 } as unknown as ClineProvider
98+ const task = createStreamingTask ( provider )
99+ vi . mocked ( provider . getCurrentTask ) . mockReturnValue ( task )
67100 new API ( { appendLine } as unknown as vscode . OutputChannel , provider , "/tmp/roo-test.sock" , true )
68101 const images = [ "data:image/png;base64,image1data" ]
102+ const executeProtectedTool = vi . fn ( )
103+ const ask = task . ask ( askType , askText , false )
104+ await vi . waitFor ( ( ) => expect ( task . clineMessages ) . toHaveLength ( 1 ) )
69105
70106 await taskCommandHandler ?.( "client-1" , {
71107 commandName : TaskCommandName . SendMessage ,
72108 data : { text : "Use this before completing" , images } ,
73109 } )
110+ const result = await ask
111+ if ( result . response === "yesButtonClicked" ) {
112+ executeProtectedTool ( )
113+ }
74114
75115 expect ( appendLine ) . toHaveBeenCalledWith ( "[API] SendMessage -> Use this before completing" )
76- expect ( addMessage ) . toHaveBeenCalledWith ( "Use this before completing" , images )
116+ expect ( result ) . toMatchObject ( { response : "messageResponse" , text : "Use this before completing" , images } )
117+ expect ( task . messageQueueService . isEmpty ( ) ) . toBe ( true )
118+ expect ( executeProtectedTool ) . not . toHaveBeenCalled ( )
77119 } )
78120
79121 it ( "logs rejected SendMessage commands without rejecting the IPC handler" , async ( ) => {
0 commit comments