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.
  final denied = _denyOverrideOutcome(toolName);
  if (denied != null) return denied;

  // 1b. A skill's disallowed-tools (turn-scoped) blocks the call.
  if (_turnDeny.contains(toolName)) {
    return ApprovalOutcome.denied(
      'Tool "$toolName" is disallowed by the active skill for this turn.',
    );
  }

  // 2. Critical bash patterns force a prompt in interactive modes
  //    (always-ask/write/yolo). In `unattended` the mode wins: there is
  //    no user to answer, so the session must never block.
  if (mode != ApprovalMode.unattended) {
    final critical = await _criticalBashOutcome(
      toolName: toolName,
      tier: tier,
      arguments: arguments,
    );
    if (critical != null) return critical;
  }

  // 3. Remaining per-tool overrides.
  final overridden = await _overrideOutcome(
    toolName: toolName,
    tier: tier,
    arguments: arguments,
  );
  if (overridden != null) return overridden;

  // 4. A skill's allowed-tools (turn-scoped) auto-allows the call.
  if (_turnAllow.contains(toolName)) {
    return const ApprovalOutcome.allowed();
  }

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

  // 6. Session mode vs. tool tier.
  return _modeOutcome(toolName: toolName, tier: tier, arguments: arguments);
}