class RubyLLM::Browser::Engine

Orchestrates one bounded browser operation.

The engine enforces, in order: egress qualification (before anything remote), destination policy, budget reservation and the actor concurrency slot, the operation lifecycle, step/byte/time limits, result redaction and bounding, and a best-effort cleanup that never reports unconfirmed closure as success.

Pre-flight failures (policy, budget, input) raise a typed {Error}. Failures after a session exists are returned as a {Result} with a typed status so the operation’s durable state is recorded first.

Constants

Attempt

Attributes

context[R]

Public Class Methods

new(context:) click to toggle source
# File lib/ruby_llm/browser/engine.rb, line 20
def initialize(context:)
  @context = context
end

Public Instance Methods

act(url:, approval_id:, action:, page_revision: nil, operation: nil) click to toggle source

One human-approved action. Disabled unless an approval store is configured and an active, exactly-bound, single-use approval exists.

# File lib/ruby_llm/browser/engine.rb, line 64
def act(url:, approval_id:, action:, page_revision: nil, operation: nil)
  unless capabilities.server_mode == :fake
    raise UnsupportedSecurityCapability, "live Act is unavailable until same-session approval is implemented"
  end
  unless capabilities.act_supported?
    raise UnsupportedSecurityCapability, "backend does not support qualified Act"
  end
  normalized = policy.authorize_action!(action)
  require_approval_store!
  execute(url: url, operation: operation) do |attempt|
    ensure_no_redirect!(attempt)
    attempt.operation = next_step(attempt.operation)
    observed = backend.observe(attempt.session, instruction: nil,
                               operation: attempt.operation, now: now)
    revision = Approval.page_revision(observed)
    if page_revision && page_revision != revision
      raise ApprovalStale, "the current page does not match the requested revision"
    end
    approval = consume_approval!(approval_id, attempt: attempt, action: normalized,
                                 page_revision: revision)
    attempt.operation = next_step(attempt.operation)
    data = backend.act(attempt.session, action: normalized, operation: attempt.operation, now: now)
    ok_result(
      attempt.operation, data, session: attempt.session,
      locator: { "url" => attempt.uri.to_s, "scope" => "act",
                 "approval_id" => approval.id, "action_digest" => approval.action_digest },
      max_bytes: nil
    )
  end
end
extract(url:, schema:, instruction: nil, max_bytes: nil, operation: nil) click to toggle source

Schema-bounded extraction from an allowlisted page.

# File lib/ruby_llm/browser/engine.rb, line 45
def extract(url:, schema:, instruction: nil, max_bytes: nil, operation: nil)
  max_bytes = optional_positive_integer!(max_bytes, :max_bytes)
  validated = policy.authorize_schema!(schema)
  execute(url: url, operation: operation) do |attempt|
    attempt.operation = next_step(attempt.operation)
    data = backend.extract(attempt.session, schema: validated, instruction: instruction,
                            operation: attempt.operation, now: now)
    policy.authorize_extraction!(data, schema: validated)
    ok_result(
      attempt.operation, data, session: attempt.session,
      locator: { "url" => current_url(attempt), "scope" => "extract",
                 "schema_digest" => Redactor.digest(validated) },
      max_bytes: max_bytes
    )
  end
end
inspect(url:, instruction: nil, max_bytes: nil, operation: nil) click to toggle source

Read-only observation of an allowlisted page.

@param operation [Operation, nil] a caller-reserved, caller-leased operation to

drive. Used by the optional Rails job so a duplicate delivery attaches to the
stored operation instead of reserving a second one. When nil the engine
reserves its own operation.
# File lib/ruby_llm/browser/engine.rb, line 30
def inspect(url:, instruction: nil, max_bytes: nil, operation: nil)
  max_bytes = optional_positive_integer!(max_bytes, :max_bytes)
  execute(url: url, operation: operation) do |attempt|
    attempt.operation = next_step(attempt.operation)
    observed = backend.observe(attempt.session, instruction: instruction,
                               operation: attempt.operation, now: now)
    ok_result(
      attempt.operation, observed, session: attempt.session,
      locator: { "url" => current_url(attempt), "scope" => "observe" },
      max_bytes: max_bytes
    )
  end
end