class RubyLLM::AgUI::Store::Base

The interface every store implements. Subclass it to get the documented contract in one place, or duck-type it.

The private helpers below are not part of the interface: they are the parts of the contract two stores would otherwise implement twice (the scope key, the clock, the replay rule). A store that duck-types the public methods has to satisfy them itself.

Constants

SCOPE_MESSAGE

Raised when a host passes a scope that cannot identify a tenant.

Public Class Methods

new(clock: nil) click to toggle source
# File lib/ruby_llm/ag_ui/store/base.rb, line 33
def initialize(clock: nil)
  @clock = clock || RubyLLM::AgUI.config.clock
end

Public Instance Methods

find(scope:, thread_id:, run_id:) click to toggle source

The record for an exact identity, or nil.

# File lib/ruby_llm/ag_ui/store/base.rb, line 48
def find(scope:, thread_id:, run_id:)
  raise NotImplementedError
end
find_interrupt(scope:, thread_id:, interrupt_id:) click to toggle source

The interrupt with id anywhere on the thread, or nil.

# File lib/ruby_llm/ag_ui/store/base.rb, line 72
def find_interrupt(scope:, thread_id:, interrupt_id:)
  raise NotImplementedError
end
latest_interrupted(scope:, thread_id:) click to toggle source

The most recent run on the thread that ended interrupted, or nil.

A resume entry may only answer an interrupt of this run; anything older is stale and must be refused.

# File lib/ruby_llm/ag_ui/store/base.rb, line 62
def latest_interrupted(scope:, thread_id:)
  raise NotImplementedError
end
latest_run(scope:, thread_id:) click to toggle source

The most recent run on the thread. Used to answer “what is this thread doing right now” without executing anything.

# File lib/ruby_llm/ag_ui/store/base.rb, line 54
def latest_run(scope:, thread_id:)
  raise NotImplementedError
end
redeem_interrupt(scope:, thread_id:, interrupt_id:, decision:, now:) click to toggle source

Atomically records decision on the interrupt with interrupt_id and returns a Redemption.

The first answer wins. A repeated answer with the same decision returns the stored interrupt as a replay, so a replayed resume request cannot run the tool twice; Redemption#recorded? says which call won. An expired interrupt, an unknown id, or a second answer that disagrees with the stored decision raises ResumeRejectedError: nothing is recorded and nothing is executed.

# File lib/ruby_llm/ag_ui/store/base.rb, line 85
def redeem_interrupt(scope:, thread_id:, interrupt_id:, decision:, now:)
  raise NotImplementedError
end
reserve(scope:, thread_id:, run_id:, digest:) click to toggle source

Claims (scope, thread_id, run_id) for digest.

Returns a Reservation whose record is the run’s authoritative state. A second call with the same key and an identical digest returns an ATTACHED reservation; a different digest raises RunConflictError, because the same run id may not mean two different requests.

# File lib/ruby_llm/ag_ui/store/base.rb, line 43
def reserve(scope:, thread_id:, run_id:, digest:)
  raise NotImplementedError
end
save(record) click to toggle source

Persists an updated record and returns it.

# File lib/ruby_llm/ag_ui/store/base.rb, line 67
def save(record)
  raise NotImplementedError
end