(
tool,
input,
toolUseContext,
assistantMessage,
toolUseId,
forceDecision,
)
| 4372 | permissionPromptTool: PermissionPromptTool, |
| 4373 | ): CanUseToolFn { |
| 4374 | const canUseTool: CanUseToolFn = async ( |
| 4375 | tool, |
| 4376 | input, |
| 4377 | toolUseContext, |
| 4378 | assistantMessage, |
| 4379 | toolUseId, |
| 4380 | forceDecision, |
| 4381 | ) => { |
| 4382 | const mainPermissionResult = |
| 4383 | forceDecision ?? |
| 4384 | (await hasPermissionsToUseTool( |
| 4385 | tool, |
| 4386 | input, |
| 4387 | toolUseContext, |
| 4388 | assistantMessage, |
| 4389 | toolUseId, |
| 4390 | )) |
| 4391 | |
| 4392 | // If the tool is allowed or denied, return the result |
| 4393 | if ( |
| 4394 | mainPermissionResult.behavior === 'allow' || |
| 4395 | mainPermissionResult.behavior === 'deny' |
| 4396 | ) { |
| 4397 | return mainPermissionResult |
| 4398 | } |
| 4399 | |
| 4400 | // Race the permission prompt tool against the abort signal. |
| 4401 | // |
| 4402 | // Why we need this: The permission prompt tool may block indefinitely waiting |
| 4403 | // for user input (e.g., via stdin or a UI dialog). If the user triggers an |
| 4404 | // interrupt (Ctrl+C), we need to detect it even while the tool is blocked. |
| 4405 | // Without this race, the abort check would only run AFTER the tool completes, |
| 4406 | // which may never happen if the tool is waiting for input that will never come. |
| 4407 | // |
| 4408 | // The second check (combinedSignal.aborted) handles a race condition where |
| 4409 | // abort fires after Promise.race resolves but before we reach this check. |
| 4410 | const { signal: combinedSignal, cleanup: cleanupAbortListener } = |
| 4411 | createCombinedAbortSignal(toolUseContext.abortController.signal) |
| 4412 | |
| 4413 | // Check if already aborted before starting the race |
| 4414 | if (combinedSignal.aborted) { |
| 4415 | cleanupAbortListener() |
| 4416 | return { |
| 4417 | behavior: 'deny', |
| 4418 | message: 'Permission prompt was aborted.', |
| 4419 | decisionReason: { |
| 4420 | type: 'permissionPromptTool' as const, |
| 4421 | permissionPromptToolName: tool.name, |
| 4422 | toolResult: undefined, |
| 4423 | }, |
| 4424 | } |
| 4425 | } |
| 4426 | |
| 4427 | const abortPromise = new Promise<'aborted'>(resolve => { |
| 4428 | combinedSignal.addEventListener('abort', () => resolve('aborted'), { |
| 4429 | once: true, |
| 4430 | }) |
| 4431 | }) |
no test coverage detected