File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -102,13 +102,17 @@ export function isExecutableToolPermissionDecision(
102102 return decision !== null && decision !== undefined && decision !== 'skip'
103103}
104104
105- /** A call held for the user's decision may run only once they allowed it. */
105+ /**
106+ * A call held for the user's decision may run only once they allowed it. A recorded decision
107+ * that does not allow it disqualifies the call even without the gate marker, which calls gated
108+ * before the marker existed lack.
109+ */
106110export function isAwaitingToolPermission ( call : {
107111 permissionRequestedAt : Date | null
108112 permissionDecision : CopilotToolPermissionDecision | null
109113} ) : boolean {
110114 return (
111- Boolean ( call . permissionRequestedAt ) &&
115+ Boolean ( call . permissionRequestedAt || call . permissionDecision ) &&
112116 ! isExecutableToolPermissionDecision ( call . permissionDecision )
113117 )
114118}
Original file line number Diff line number Diff line change @@ -728,7 +728,10 @@ export async function claimToolExecution(
728728 thisCall ,
729729 eq ( copilotAsyncToolCalls . status , ASYNC_TOOL_STATUS . pending ) ,
730730 or (
731- isNull ( copilotAsyncToolCalls . permissionRequestedAt ) ,
731+ and (
732+ isNull ( copilotAsyncToolCalls . permissionRequestedAt ) ,
733+ isNull ( copilotAsyncToolCalls . permissionDecision )
734+ ) ,
732735 inArray ( copilotAsyncToolCalls . permissionDecision , [
733736 ...EXECUTABLE_TOOL_PERMISSION_DECISIONS ,
734737 ] )
Original file line number Diff line number Diff line change @@ -260,6 +260,22 @@ describe.runIf(Boolean(redisUrl))('desktop tool calls the server no longer admit
260260 expect ( await storedCall ( toolCallId ) ) . toMatchObject ( { status : 'pending' , claimedBy : null } )
261261 } )
262262
263+ it ( 'refuses a call the user declined before its gate was recorded on the row' , async ( ) => {
264+ const { runId } = await startRun ( )
265+ const toolCallId = generateId ( )
266+ await db . insert ( copilotAsyncToolCalls ) . values ( {
267+ runId,
268+ toolCallId,
269+ toolName : 'terminal' ,
270+ args : { operation : 'run' , args : { command : 'git push --force' } } ,
271+ status : 'pending' ,
272+ permissionDecision : 'skip' ,
273+ } )
274+
275+ expect ( ( await desktopClaims ( toolCallId ) ) . status ) . toBe ( 403 )
276+ expect ( await storedCall ( toolCallId ) ) . toMatchObject ( { status : 'pending' , claimedBy : null } )
277+ } )
278+
263279 it ( 'refuses a pending desktop call after Stop, and settles it as never started' , async ( ) => {
264280 const { runId, streamId } = await startRun ( )
265281 const toolCallId = await agentCalls ( runId , 'browser_click' , { ref : 'e12' } )
You can’t perform that action at this time.
0 commit comments