authorize method

Future<ApprovalOutcome> authorize({
  1. required String toolName,
  2. required ApprovalTier tier,
  3. required Map<String, dynamic> arguments,
})

Resolves the policy for one tool call and, when it lands on ApprovalPolicy.prompt, awaits the user's decision via prompt.

Implementation

Future<ApprovalOutcome> authorize({
  required String toolName,
  required ApprovalTier tier,
  required Map<String, dynamic> arguments,
}) async {
  // 1. An explicit deny wins over everything, the interceptor included.
  if (_overrides[toolName] == ApprovalPolicy.deny) {
    return ApprovalOutcome.denied(
      'Tool "$toolName" is denied by the configured approval policy.',
    );
  }

  // 2. Critical bash patterns force a prompt regardless of mode or
  //    allow-policy — even under yolo and after "approve always".
  if (toolName == bashToolName) {
    final command = arguments['command'];
    final label = command is String
        ? matchCriticalBashCommand(command)
        : null;
    if (label != null) {
      return _requestDecision(
        toolName: toolName,
        tier: tier,
        arguments: arguments,
        reason: 'Critical pattern detected: $label',
      );
    }
  }

  // 3. Remaining per-tool overrides.
  switch (_overrides[toolName]) {
    case ApprovalPolicy.allow:
      return const ApprovalOutcome.allowed();
    case ApprovalPolicy.prompt:
      return _requestDecision(
        toolName: toolName,
        tier: tier,
        arguments: arguments,
        reason: 'Tool "$toolName" is set to always ask for approval',
      );
    case ApprovalPolicy.deny || null:
      break; // deny was handled first; nothing overrides here.
  }

  // 4. Session always-allow ("approve always" answers, `/allow`).
  if (_alwaysAllow.contains(toolName)) {
    return const ApprovalOutcome.allowed();
  }

  // 5. Session mode vs. tool tier.
  switch (mode) {
    case ApprovalMode.yolo:
      return const ApprovalOutcome.allowed();
    case ApprovalMode.write:
      if (tier == ApprovalTier.read) return const ApprovalOutcome.allowed();
      return _requestDecision(
        toolName: toolName,
        tier: tier,
        arguments: arguments,
        reason: 'Mode "write" requires approval for ${tier.name}-tier tools',
      );
    case ApprovalMode.alwaysAsk:
      return _requestDecision(
        toolName: toolName,
        tier: tier,
        arguments: arguments,
        reason: 'Mode "always-ask" requires approval for every tool call',
      );
  }
}