Class: PermissionRequest
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- PermissionRequest
- Defined in:
- app/models/permission_request.rb
Overview
One "may I run this?" from an agent in ask-first mode (ยง permissions). The claude process is ALIVE and blocked while this is pending โ the MCP shim polls for the verdict and the poll keeps the run's heartbeat fresh.
Constant Summary collapse
- STATUSES =
%w[pending allowed denied auto_denied].freeze
Instance Method Summary collapse
-
#auto_allowed? ⇒ Boolean
Does an existing pattern (column pre-approved list or earlier allow-for-this-run) already cover this request?.
- #describe ⇒ Object
- #pending? ⇒ Boolean
-
#resolve!(verdict, message: nil, always: false, actor: "user") ⇒ Object
The single verdict path โ buttons, chat replies, and the shim's timeout all land here.
Instance Method Details
#auto_allowed? ⇒ Boolean
Does an existing pattern (column pre-approved list or earlier allow-for-this-run) already cover this request?
46 47 48 49 50 51 52 53 |
# File 'app/models/permission_request.rb', line 46 def auto_allowed? patterns = Array(run.card.column.policy["allowed_commands"]) + Array(run.briefing["allowed_patterns"]) if tool_name == "Bash" patterns.any? { |p| p.present? && command.to_s.start_with?(p.strip) } else patterns.map(&:strip).include?(tool_name) end end |
#describe ⇒ Object
15 16 17 |
# File 'app/models/permission_request.rb', line 15 def describe tool_name == "Bash" && command.present? ? "`#{command.truncate(120)}`" : tool_name end |
#pending? ⇒ Boolean
13 |
# File 'app/models/permission_request.rb', line 13 def pending? = status == "pending" |
#resolve!(verdict, message: nil, always: false, actor: "user") ⇒ Object
The single verdict path โ buttons, chat replies, and the shim's timeout
all land here. always remembers the pattern for the rest of the run.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'app/models/permission_request.rb', line 21 def resolve!(verdict, message: nil, always: false, actor: "user") return unless pending? update!(status: verdict, message: .presence, answered_at: Time.current) card = run.card if verdict == "allowed" && always pattern = tool_name == "Bash" ? command.to_s : tool_name patterns = Array(run.briefing["allowed_patterns"]) | [pattern] run.update!(briefing: run.briefing.merge("allowed_patterns" => patterns)) end text = case verdict when "allowed" then "๐ Approved #{describe}#{" โ and anything matching it for the rest of this run" if always}" when "auto_denied" then "๐ #{describe} auto-denied (no answer in time) โ the agent was told to adapt or park" else "๐ Denied #{describe}#{": โ#{}โ" if .present?}" end card.log!("status_change", actor: actor, run: run, text: text) card.update!(status: "working") if card.needs_input? && run..pending.none? broadcast_resolution end |