class RubyLLM::Browser::Rails::ActiveRecordApprovalStore

Durable {RubyLLM::Browser::ApprovalStore} backed by ActiveRecord.

{#consume_once} performs every check and uses a conditional database update: existence, active, actor/run, URL, page revision, operation/session and exact action digest — before it marks the row consumed. Two workers cannot both consume the same grant, and a replayed or cross-actor approval is refused.

Public Class Methods

new(record_class: ApprovalRecord) click to toggle source
Calls superclass method
# File lib/ruby_llm/browser/rails/active_record_approval_store.rb, line 13
def initialize(record_class: ApprovalRecord)
  super()
  @record_class = record_class
end

Public Instance Methods

consume_once(id, actor_id:, run_id:, url:, action_digest:, now:, page_revision: nil, operation_id: nil, session_id: nil) click to toggle source
# File lib/ruby_llm/browser/rails/active_record_approval_store.rb, line 38
def consume_once(id, actor_id:, run_id:, url:, action_digest:, now:, page_revision: nil,
                 operation_id: nil, session_id: nil)
  record = @record_class.find_by(id: id)
  raise ApprovalRequired, "no approval was issued" unless record
    approval = record.to_approval
    raise ApprovalStale, "approval #{id} was already consumed" if approval.consumed?
    raise ApprovalStale, "approval #{id} was revoked" if approval.revoked?
    raise ApprovalStale, "approval #{id} has expired" if approval.expired?(now)

    unless approval.binding_matches?(
      actor_id: actor_id, run_id: run_id, url: url, action_digest: action_digest,
      page_revision: page_revision, operation_id: operation_id, session_id: session_id
    )
      raise ApprovalStale, "approval #{id} does not authorize this actor, URL, page or action"
    end

    consumed = approval.consume!(now: now)
    changed = @record_class.where(id: id, consumed_at: nil, revoked: false)
                           .where("expires_at > ?", now)
                           .update_all(consumed_at: now, updated_at: now)
    raise ApprovalStale, "approval is no longer active" unless changed == 1

    consumed
end
fetch(id) click to toggle source
# File lib/ruby_llm/browser/rails/active_record_approval_store.rb, line 34
def fetch(id)
  @record_class.find_by(id: id)&.to_approval
end
issue(approval) click to toggle source
# File lib/ruby_llm/browser/rails/active_record_approval_store.rb, line 18
def issue(approval)
  @record_class.create!(
    id: approval.id, actor_id: approval.actor_id, run_id: approval.run_id,
    url: approval.url, action_digest: approval.action_digest,
    action: @record_class.dump_action(approval.action),
    page_revision: approval.page_revision, operation_id: approval.operation_id,
    session_id: approval.session_id, issued_at: approval.issued_at,
    expires_at: approval.expires_at, consumed_at: approval.consumed_at,
    revoked: approval.revoked?, created_at: approval.issued_at,
    updated_at: approval.issued_at
  )
  approval
rescue ActiveRecord::RecordNotUnique
  raise InvalidInput, "approval #{approval.id} already exists"
end
revoke(id) click to toggle source
# File lib/ruby_llm/browser/rails/active_record_approval_store.rb, line 63
def revoke(id)
  changed = @record_class.where(id: id).update_all(revoked: true, updated_at: Time.now.utc)
  raise ApprovalRequired, "no approval was issued" if changed.zero?

  fetch(id)
end