Class: Ask::Agent::Policies::PermissionRules

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/agent/policies/permission_rules.rb

Overview

Persisted, matchable allow/ask/deny patterns for tool calls.

Rules classify a tool call before it executes or prompts:

rules = Ask::Agent::Policies::PermissionRules.new do |r|
r.allow :bash, /^git (pull|push|status)/
r.ask   :bash, /^rm -rf/
r.deny  :write, %r{/\.env(\.local)?$}
r.ask   :destroy, :all
end

session = Ask::Agent::Session.new(
model: "gpt-4o",
approval: { rules: rules }
)

Classification is first-match-wins, in declaration order: :allow runs the tool, :ask queues it for human approval, :deny blocks it. Rules take precedence over a tool's own approval_required / auto_approvable declarations — they are explicit user intent.

Dangerous-rule guard: an :allow rule for a code-executing tool (bash, code, repl) whose argument pattern is unrestricted would let the model run anything without asking. Such rules are downgraded to :ask unless the ruleset was created with auto_allow_dangerous: true — "approve once, remember the pattern" must not become "approve everything".

Defined Under Namespace

Classes: Rule

Constant Summary collapse

DANGEROUS_TOOLS =

Tools that execute arbitrary code — an unrestricted :allow rule on any of these is dangerous.

%i[bash code repl].freeze

Instance Method Summary collapse

Constructor Details

#initialize(auto_allow_dangerous: false, &block) ⇒ PermissionRules

Returns a new instance of PermissionRules.

Parameters:

  • auto_allow_dangerous (Boolean) (defaults to: false)

    keep unrestricted :allow rules on code-executing tools (default false — they downgrade to :ask)



71
72
73
74
75
# File 'lib/ask/agent/policies/permission_rules.rb', line 71

def initialize(auto_allow_dangerous: false, &block)
  @auto_allow_dangerous = auto_allow_dangerous
  @rules = []
  instance_eval(&block) if block
end

Instance Method Details

#allow(tool_pattern, argument_pattern = nil) ⇒ Object

DSL — declare rules in priority order (first match wins).



78
79
80
# File 'lib/ask/agent/policies/permission_rules.rb', line 78

def allow(tool_pattern, argument_pattern = nil)
  add(:allow, tool_pattern, argument_pattern)
end

#ask(tool_pattern, argument_pattern = nil) ⇒ Object



82
83
84
# File 'lib/ask/agent/policies/permission_rules.rb', line 82

def ask(tool_pattern, argument_pattern = nil)
  add(:ask, tool_pattern, argument_pattern)
end

#classify(tool_name, arguments = nil) ⇒ Symbol?

Classify a tool call.

Parameters:

  • tool_name (String)
  • arguments (Hash, String, nil) (defaults to: nil)

    tool arguments (hash or JSON)

Returns:

  • (Symbol, nil)

    :allow, :ask, :deny — or nil when no rule matches



99
100
101
102
103
104
105
106
107
108
# File 'lib/ask/agent/policies/permission_rules.rb', line 99

def classify(tool_name, arguments = nil)
  rule = @rules.find { |r| r.matches?(tool_name, arguments) }
  return nil unless rule

  if rule.decision == :allow && dangerous?(rule) && !@auto_allow_dangerous
    :ask
  else
    rule.decision
  end
end

#dangerous_rulesArray<Rule>

Returns rules that would allow unrestricted execution of a code-executing tool.

Returns:

  • (Array<Rule>)

    rules that would allow unrestricted execution of a code-executing tool



112
113
114
# File 'lib/ask/agent/policies/permission_rules.rb', line 112

def dangerous_rules
  @rules.select { |r| dangerous?(r) }
end

#deny(tool_pattern, argument_pattern = nil) ⇒ Object



86
87
88
# File 'lib/ask/agent/policies/permission_rules.rb', line 86

def deny(tool_pattern, argument_pattern = nil)
  add(:deny, tool_pattern, argument_pattern)
end

#rulesArray<Rule>

Returns declared rules, in order.

Returns:

  • (Array<Rule>)

    declared rules, in order



91
# File 'lib/ask/agent/policies/permission_rules.rb', line 91

def rules = @rules.dup