@@ -170,6 +170,103 @@ test("computeToolCallPermissions only asks for scopes not already allowed", () =
170170 ) ;
171171} ) ;
172172
173+ test ( "computeToolCallPermissions temporarily upgrades allowed forced scopes to ask" , ( ) => {
174+ const projectRoot = createTempDir ( "deepcode-permissions-force-ask-workspace-" ) ;
175+ const forcedScopes : PermissionScope [ ] = [
176+ "write-in-cwd" ,
177+ "write-out-cwd" ,
178+ "delete-in-cwd" ,
179+ "delete-out-cwd" ,
180+ "mutate-git-log" ,
181+ ] ;
182+ const plan = computeToolCallPermissions ( {
183+ sessionId : "session-1" ,
184+ projectRoot,
185+ forceAskScopes : forcedScopes ,
186+ settings : {
187+ allow : [ "write-in-cwd" , "write-out-cwd" , "delete-out-cwd" , "mutate-git-log" ] as PermissionScope [ ] ,
188+ deny : [ "delete-in-cwd" ] as PermissionScope [ ] ,
189+ ask : [ ] as PermissionScope [ ] ,
190+ defaultMode : "allowAll" as const ,
191+ } ,
192+ toolCalls : [
193+ {
194+ id : "call-write-in" ,
195+ type : "function" ,
196+ function : { name : "write" , arguments : JSON . stringify ( { file_path : path . join ( projectRoot , "file.txt" ) } ) } ,
197+ } ,
198+ {
199+ id : "call-write-out" ,
200+ type : "function" ,
201+ function : { name : "write" , arguments : JSON . stringify ( { file_path : "/tmp/file.txt" } ) } ,
202+ } ,
203+ {
204+ id : "call-delete-out" ,
205+ type : "function" ,
206+ function : {
207+ name : "bash" ,
208+ arguments : JSON . stringify ( {
209+ command : "rm /tmp/file.txt" ,
210+ sideEffects : [ "delete-out-cwd" ] ,
211+ } ) ,
212+ } ,
213+ } ,
214+ {
215+ id : "call-mutate-git" ,
216+ type : "function" ,
217+ function : {
218+ name : "bash" ,
219+ arguments : JSON . stringify ( { command : "git commit --allow-empty -m test" , sideEffects : [ "mutate-git-log" ] } ) ,
220+ } ,
221+ } ,
222+ {
223+ id : "call-delete-in" ,
224+ type : "function" ,
225+ function : {
226+ name : "bash" ,
227+ arguments : JSON . stringify ( { command : "rm file.txt" , sideEffects : [ "delete-in-cwd" ] } ) ,
228+ } ,
229+ } ,
230+ ] ,
231+ } ) ;
232+
233+ assert . deepEqual ( plan . permissions , [
234+ { toolCallId : "call-write-in" , permission : "ask" } ,
235+ { toolCallId : "call-write-out" , permission : "ask" } ,
236+ { toolCallId : "call-delete-out" , permission : "ask" } ,
237+ { toolCallId : "call-mutate-git" , permission : "ask" } ,
238+ { toolCallId : "call-delete-in" , permission : "deny" } ,
239+ ] ) ;
240+ assert . deepEqual (
241+ plan . askPermissions . map ( ( item ) => ( { id : item . toolCallId , scopes : item . scopes } ) ) ,
242+ [
243+ { id : "call-write-in" , scopes : [ "write-in-cwd" ] } ,
244+ { id : "call-write-out" , scopes : [ "write-out-cwd" ] } ,
245+ { id : "call-delete-out" , scopes : [ "delete-out-cwd" ] } ,
246+ { id : "call-mutate-git" , scopes : [ "mutate-git-log" ] } ,
247+ ]
248+ ) ;
249+
250+ const defaultPlan = computeToolCallPermissions ( {
251+ sessionId : "session-1" ,
252+ projectRoot,
253+ settings : {
254+ allow : [ "write-in-cwd" ] as PermissionScope [ ] ,
255+ deny : [ ] as PermissionScope [ ] ,
256+ ask : [ ] as PermissionScope [ ] ,
257+ defaultMode : "allowAll" as const ,
258+ } ,
259+ toolCalls : [
260+ {
261+ id : "call-default" ,
262+ type : "function" ,
263+ function : { name : "write" , arguments : JSON . stringify ( { file_path : path . join ( projectRoot , "file.txt" ) } ) } ,
264+ } ,
265+ ] ,
266+ } ) ;
267+ assert . deepEqual ( defaultPlan . permissions , [ { toolCallId : "call-default" , permission : "allow" } ] ) ;
268+ } ) ;
269+
173270test ( "computeToolCallPermissions allows read tool calls under skill scan paths" , ( ) => {
174271 const projectRoot = createTempDir ( "deepcode-permissions-skill-read-workspace-" ) ;
175272 const home = createTempDir ( "deepcode-permissions-skill-read-home-" ) ;
0 commit comments