1- import { type ClineAsk , type McpServerUse , isNonBlockingAsk } from "@roo-code/types"
1+ import { type ClineAsk , type McpServerUse , type FollowUpData , isNonBlockingAsk } from "@roo-code/types"
22
33import type { ClineSayTool , ExtensionState } from "../../shared/ExtensionMessage"
4+ import { ClineAskResponse } from "../../shared/WebviewMessage"
45
56import { isWriteToolAction , isReadOnlyToolAction } from "./tools"
67import { isMcpToolAlwaysAllowed } from "./mcp"
@@ -25,77 +26,106 @@ export type AutoApprovalStateOptions =
2526 | "alwaysAllowReadOnlyOutsideWorkspace" // For `alwaysAllowReadOnly`.
2627 | "alwaysAllowWriteOutsideWorkspace" // For `alwaysAllowWrite`.
2728 | "alwaysAllowWriteProtected"
29+ | "followupAutoApproveTimeoutMs" // For `alwaysAllowFollowupQuestions`.
2830 | "mcpServers" // For `alwaysAllowMcp`.
2931 | "allowedCommands" // For `alwaysAllowExecute`.
3032 | "deniedCommands"
3133
32- export type CheckAutoApprovalResult = "approve" | "deny" | "ask"
34+ export type CheckAutoApprovalResult =
35+ | { decision : "approve" }
36+ | { decision : "deny" }
37+ | { decision : "ask" }
38+ | {
39+ decision : "timeout"
40+ timeout : number
41+ fn : ( ) => { askResponse : ClineAskResponse ; text ?: string ; images ?: string [ ] }
42+ }
3343
3444export async function checkAutoApproval ( {
3545 state,
3646 ask,
3747 text,
3848 isProtected,
3949} : {
40- state : Pick < ExtensionState , AutoApprovalState | AutoApprovalStateOptions >
50+ state ? : Pick < ExtensionState , AutoApprovalState | AutoApprovalStateOptions >
4151 ask : ClineAsk
4252 text ?: string
4353 isProtected ?: boolean
4454} ) : Promise < CheckAutoApprovalResult > {
4555 if ( isNonBlockingAsk ( ask ) ) {
46- return "approve"
56+ return { decision : "approve" }
4757 }
4858
49- if ( ! state . autoApprovalEnabled ) {
50- return "ask"
59+ if ( ! state || ! state . autoApprovalEnabled ) {
60+ return { decision : "ask" }
5161 }
5262
53- // Note: The `alwaysApproveResubmit` check is already handled in `Task`.
54-
5563 if ( ask === "followup" ) {
56- return state . alwaysAllowFollowupQuestions === true ? "approve" : "ask"
64+ if ( state . alwaysAllowFollowupQuestions === true ) {
65+ try {
66+ const suggestion = ( JSON . parse ( text || "{}" ) as FollowUpData ) . suggest ?. [ 0 ]
67+
68+ if (
69+ suggestion &&
70+ typeof state . followupAutoApproveTimeoutMs === "number" &&
71+ state . followupAutoApproveTimeoutMs > 0
72+ ) {
73+ return {
74+ decision : "timeout" ,
75+ timeout : state . followupAutoApproveTimeoutMs ,
76+ fn : ( ) => ( { askResponse : "messageResponse" , text : suggestion . answer } ) ,
77+ }
78+ } else {
79+ return { decision : "ask" }
80+ }
81+ } catch ( error ) {
82+ return { decision : "ask" }
83+ }
84+ } else {
85+ return { decision : "ask" }
86+ }
5787 }
5888
5989 if ( ask === "browser_action_launch" ) {
60- return state . alwaysAllowBrowser === true ? "approve" : "ask"
90+ return state . alwaysAllowBrowser === true ? { decision : "approve" } : { decision : "ask" }
6191 }
6292
6393 if ( ask === "use_mcp_server" ) {
6494 if ( ! text ) {
65- return "ask"
95+ return { decision : "ask" }
6696 }
6797
6898 try {
6999 const mcpServerUse = JSON . parse ( text ) as McpServerUse
70100
71101 if ( mcpServerUse . type === "use_mcp_tool" ) {
72102 return state . alwaysAllowMcp === true && isMcpToolAlwaysAllowed ( mcpServerUse , state . mcpServers )
73- ? "approve"
74- : "ask"
103+ ? { decision : "approve" }
104+ : { decision : "ask" }
75105 } else if ( mcpServerUse . type === "access_mcp_resource" ) {
76- return state . alwaysAllowMcp === true ? "approve" : "ask"
106+ return state . alwaysAllowMcp === true ? { decision : "approve" } : { decision : "ask" }
77107 }
78108 } catch ( error ) {
79- return "ask"
109+ return { decision : "ask" }
80110 }
81111
82- return "ask"
112+ return { decision : "ask" }
83113 }
84114
85115 if ( ask === "command" ) {
86116 if ( ! text ) {
87- return "ask"
117+ return { decision : "ask" }
88118 }
89119
90120 if ( state . alwaysAllowExecute === true ) {
91121 const decision = getCommandDecision ( text , state . allowedCommands || [ ] , state . deniedCommands || [ ] )
92122
93123 if ( decision === "auto_approve" ) {
94- return "approve"
124+ return { decision : "approve" }
95125 } else if ( decision === "auto_deny" ) {
96- return "deny"
126+ return { decision : "deny" }
97127 } else {
98- return "ask"
128+ return { decision : "ask" }
99129 }
100130 }
101131 }
@@ -110,50 +140,50 @@ export async function checkAutoApproval({
110140 }
111141
112142 if ( ! tool ) {
113- return "ask"
143+ return { decision : "ask" }
114144 }
115145
116146 if ( tool . tool === "updateTodoList" ) {
117- return state . alwaysAllowUpdateTodoList === true ? "approve" : "ask"
147+ return state . alwaysAllowUpdateTodoList === true ? { decision : "approve" } : { decision : "ask" }
118148 }
119149
120150 if ( tool ?. tool === "fetchInstructions" ) {
121151 if ( tool . content === "create_mode" ) {
122- return state . alwaysAllowModeSwitch === true ? "approve" : "ask"
152+ return state . alwaysAllowModeSwitch === true ? { decision : "approve" } : { decision : "ask" }
123153 }
124154
125155 if ( tool . content === "create_mcp_server" ) {
126- return state . alwaysAllowMcp === true ? "approve" : "ask"
156+ return state . alwaysAllowMcp === true ? { decision : "approve" } : { decision : "ask" }
127157 }
128158 }
129159
130160 if ( tool ?. tool === "switchMode" ) {
131- return state . alwaysAllowModeSwitch === true ? "approve" : "ask"
161+ return state . alwaysAllowModeSwitch === true ? { decision : "approve" } : { decision : "ask" }
132162 }
133163
134164 if ( [ "newTask" , "finishTask" ] . includes ( tool ?. tool ) ) {
135- return state . alwaysAllowSubtasks === true ? "approve" : "ask"
165+ return state . alwaysAllowSubtasks === true ? { decision : "approve" } : { decision : "ask" }
136166 }
137167
138168 const isOutsideWorkspace = ! ! tool . isOutsideWorkspace
139169
140170 if ( isReadOnlyToolAction ( tool ) ) {
141171 return state . alwaysAllowReadOnly === true &&
142172 ( ! isOutsideWorkspace || state . alwaysAllowReadOnlyOutsideWorkspace === true )
143- ? "approve"
144- : "ask"
173+ ? { decision : "approve" }
174+ : { decision : "ask" }
145175 }
146176
147177 if ( isWriteToolAction ( tool ) ) {
148178 return state . alwaysAllowWrite === true &&
149179 ( ! isOutsideWorkspace || state . alwaysAllowWriteOutsideWorkspace === true ) &&
150180 ( ! isProtected || state . alwaysAllowWriteProtected === true )
151- ? "approve"
152- : "ask"
181+ ? { decision : "approve" }
182+ : { decision : "ask" }
153183 }
154184 }
155185
156- return "ask"
186+ return { decision : "ask" }
157187}
158188
159189export { AutoApprovalHandler } from "./AutoApprovalHandler"
0 commit comments