(
tool,
input,
toolUseContext,
assistantMessage,
toolUseId,
forceDecision,
)
| 4184 | permissionPromptTool: PermissionPromptTool, |
| 4185 | ): CanUseToolFn { |
| 4186 | const canUseTool: CanUseToolFn = async ( |
| 4187 | tool, |
| 4188 | input, |
| 4189 | toolUseContext, |
| 4190 | assistantMessage, |
| 4191 | toolUseId, |
| 4192 | forceDecision, |
| 4193 | ) => { |
| 4194 | const mainPermissionResult = |
| 4195 | forceDecision ?? |
| 4196 | (await hasPermissionsToUseTool( |
| 4197 | tool, |
| 4198 | input, |
| 4199 | toolUseContext, |
| 4200 | assistantMessage, |
| 4201 | toolUseId, |
| 4202 | )) |
| 4203 | |
| 4204 | // If the tool is allowed or denied, return the result |
| 4205 | if ( |
| 4206 | mainPermissionResult.behavior === 'allow' || |
| 4207 | mainPermissionResult.behavior === 'deny' |
| 4208 | ) { |
| 4209 | return mainPermissionResult |
| 4210 | } |
| 4211 | |
| 4212 | // Race the permission prompt tool against the abort signal. |
| 4213 | // |
| 4214 | // Why we need this: The permission prompt tool may block indefinitely waiting |
| 4215 | // for user input (e.g., via stdin or a UI dialog). If the user triggers an |
| 4216 | // interrupt (Ctrl+C), we need to detect it even while the tool is blocked. |
| 4217 | // Without this race, the abort check would only run AFTER the tool completes, |
| 4218 | // which may never happen if the tool is waiting for input that will never come. |
| 4219 | // |
| 4220 | // The second check (combinedSignal.aborted) handles a race condition where |
| 4221 | // abort fires after Promise.race resolves but before we reach this check. |
| 4222 | const { signal: combinedSignal, cleanup: cleanupAbortListener } = |
| 4223 | createCombinedAbortSignal(toolUseContext.abortController.signal) |
| 4224 | |
| 4225 | // Check if already aborted before starting the race |
| 4226 | if (combinedSignal.aborted) { |
| 4227 | cleanupAbortListener() |
| 4228 | return { |
| 4229 | behavior: 'deny', |
| 4230 | message: 'Permission prompt was aborted.', |
| 4231 | decisionReason: { |
| 4232 | type: 'permissionPromptTool' as const, |
| 4233 | permissionPromptToolName: tool.name, |
| 4234 | toolResult: undefined, |
| 4235 | }, |
| 4236 | } |
| 4237 | } |
| 4238 | |
| 4239 | const abortPromise = new Promise<'aborted'>(resolve => { |
| 4240 | combinedSignal.addEventListener('abort', () => resolve('aborted'), { |
| 4241 | once: true, |
| 4242 | }) |
| 4243 | }) |
no test coverage detected