resolveHookPermissionDecision function
Future<({PermissionResult decision, Map<String, dynamic> input})>
resolveHookPermissionDecision({
- required PermissionResult? hookPermissionResult,
- required ToolDefinition tool,
- required Map<
String, dynamic> input, - required ToolUseContext toolUseContext,
- required CanUseToolFn canUseTool,
- required AssistantMessage assistantMessage,
- required String toolUseID,
- Future<
PermissionResult?> checkRuleBasedPermissions(- ToolDefinition,
- Map<
String, dynamic> , - ToolUseContext
Resolve a PreToolUse hook's permission result into a final decision.
Encapsulates the invariant that hook 'allow' does NOT bypass settings.json deny/ask rules.
Implementation
Future<({PermissionResult decision, Map<String, dynamic> input})>
resolveHookPermissionDecision({
required PermissionResult? hookPermissionResult,
required ToolDefinition tool,
required Map<String, dynamic> input,
required ToolUseContext toolUseContext,
required CanUseToolFn canUseTool,
required AssistantMessage assistantMessage,
required String toolUseID,
Future<PermissionResult?> Function(
ToolDefinition,
Map<String, dynamic>,
ToolUseContext,
)?
checkRuleBasedPermissions,
}) async {
final requiresInteraction = tool.requiresUserInteraction?.call() ?? false;
final requireCanUseTool = toolUseContext.requireCanUseTool;
if (hookPermissionResult?.behavior == PermissionBehavior.allow) {
final hookInput = hookPermissionResult!.updatedInput ?? input;
final interactionSatisfied =
requiresInteraction && hookPermissionResult.updatedInput != null;
if ((requiresInteraction && !interactionSatisfied) || requireCanUseTool) {
return (
decision: await canUseTool(
tool,
hookInput,
toolUseContext,
assistantMessage,
toolUseID,
),
input: hookInput,
);
}
// Hook allow skips interactive prompt, but deny/ask rules still apply
if (checkRuleBasedPermissions != null) {
final ruleCheck = await checkRuleBasedPermissions(
tool,
hookInput,
toolUseContext,
);
if (ruleCheck != null) {
if (ruleCheck.behavior == PermissionBehavior.deny) {
return (decision: ruleCheck, input: hookInput);
}
// ask rule — dialog required despite hook approval
return (
decision: await canUseTool(
tool,
hookInput,
toolUseContext,
assistantMessage,
toolUseID,
),
input: hookInput,
);
}
}
return (decision: hookPermissionResult, input: hookInput);
}
if (hookPermissionResult?.behavior == PermissionBehavior.deny) {
return (decision: hookPermissionResult!, input: input);
}
// No hook decision or 'ask' — normal permission flow
final forceDecision = hookPermissionResult?.behavior == PermissionBehavior.ask
? hookPermissionResult
: null;
final askInput =
(hookPermissionResult?.behavior == PermissionBehavior.ask &&
hookPermissionResult?.updatedInput != null)
? hookPermissionResult!.updatedInput!
: input;
return (
decision: await canUseTool(
tool,
askInput,
toolUseContext,
assistantMessage,
toolUseID,
forceDecision,
),
input: askInput,
);
}