Class: Ask::AppServer::PermissionHandler
- Inherits:
-
Object
- Object
- Ask::AppServer::PermissionHandler
- Defined in:
- lib/ask/app_server/permission_handler.rb
Overview
Protocol-aware permission handler that integrates with ask-agent's before_tool_call hook system.
When a blocked tool is called, this handler:
1. Sends an `interaction/requestPermission` protocol message to the client
2. Blocks the tool thread until the client responds (or timeout)
3. Returns { action: :proceed } if approved, { action: :block } if denied
Usage:
handler = PermissionHandler.new(mode: :on_request)
handler.on_request { |req_id, tool_name, args| send_protocol_message(...) }
# Wire into ask-agent session
session = Ask::Agent::Session.new(hooks: { before_tool: [handler] })
# When the client responds:
handler.handle_response(request_id, "approve")
Constant Summary collapse
- DEFAULT_BLOCKED_TOOLS =
Default tools that require permission.
%i[write edit bash destroy].freeze
- DEFAULT_TIMEOUT =
Default timeout in seconds.
300
Instance Attribute Summary collapse
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
Instance Method Summary collapse
-
#before_tool_call(tool_call, _context = {}) ⇒ Object
Hook interface for Ask::Agent::Session's before_tool_call chain.
-
#cancel_all! ⇒ Object
Cancel all pending permission requests (e.g., on session shutdown).
-
#handle_response(request_id, decision, reason: nil) ⇒ Object
Called by the server when the client responds to a permission request.
-
#initialize(mode: :on_request, blocked_tools: nil, timeout: DEFAULT_TIMEOUT) ⇒ PermissionHandler
constructor
A new instance of PermissionHandler.
-
#on_request(&block) ⇒ Object
Register a callback for sending the protocol message.
-
#pending? ⇒ Boolean
Are there any pending permission requests?.
-
#pending_count ⇒ Object
Number of pending permission requests.
Constructor Details
#initialize(mode: :on_request, blocked_tools: nil, timeout: DEFAULT_TIMEOUT) ⇒ PermissionHandler
Returns a new instance of PermissionHandler.
36 37 38 39 40 41 42 43 44 |
# File 'lib/ask/app_server/permission_handler.rb', line 36 def initialize(mode: :on_request, blocked_tools: nil, timeout: DEFAULT_TIMEOUT) @mode = mode @blocked_tools = (blocked_tools || DEFAULT_BLOCKED_TOOLS).map(&:to_sym) @timeout = timeout @pending = {} @sender = nil @mutex = Mutex.new @logger = Logger.new($stdout, level: ENV["DEBUG"] ? Logger::DEBUG : Logger::WARN) end |
Instance Attribute Details
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
31 32 33 |
# File 'lib/ask/app_server/permission_handler.rb', line 31 def mode @mode end |
Instance Method Details
#before_tool_call(tool_call, _context = {}) ⇒ Object
Hook interface for Ask::Agent::Session's before_tool_call chain. Returns { action: :proceed } or { action: :block, reason: "..." }.
54 55 56 57 58 59 |
# File 'lib/ask/app_server/permission_handler.rb', line 54 def before_tool_call(tool_call, _context = {}) return { action: :proceed } unless @blocked_tools.include?(tool_call.name.to_sym) return { action: :proceed } if @mode == :never request_approval(tool_call) end |
#cancel_all! ⇒ Object
Cancel all pending permission requests (e.g., on session shutdown).
80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/ask/app_server/permission_handler.rb', line 80 def cancel_all! @mutex.synchronize do @pending.each_value do |entry| entry[:responded] = true entry[:approved] = false entry[:reason] = "Permission request cancelled" entry[:condition].signal end @pending.clear end end |
#handle_response(request_id, decision, reason: nil) ⇒ Object
Called by the server when the client responds to a permission request.
66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/ask/app_server/permission_handler.rb', line 66 def handle_response(request_id, decision, reason: nil) @mutex.synchronize do entry = @pending[request_id] return false unless entry entry[:responded] = true entry[:approved] = (decision.to_s == "approve") entry[:reason] = reason entry[:condition].signal true end end |
#on_request(&block) ⇒ Object
Register a callback for sending the protocol message. The callback receives (request_id, tool_name, tool_arguments).
48 49 50 |
# File 'lib/ask/app_server/permission_handler.rb', line 48 def on_request(&block) @sender = block end |
#pending? ⇒ Boolean
Are there any pending permission requests?
98 99 100 |
# File 'lib/ask/app_server/permission_handler.rb', line 98 def pending? pending_count > 0 end |
#pending_count ⇒ Object
Number of pending permission requests.
93 94 95 |
# File 'lib/ask/app_server/permission_handler.rb', line 93 def pending_count @mutex.synchronize { @pending.size } end |