Class: Agent::Sessions::Adapters::Base

Inherits:
Object
  • Object
show all
Includes:
Enumeration, HomeExpansion
Defined in:
lib/agent/sessions/adapters/base.rb

Overview

Base class for every agent adapter. Subclass this directly, never another adapter: the DSL keeps its configuration in singleton instance variables, which Ruby does not carry down a second level of inheritance, so a subclass of a subclass would silently declare nothing.

An instance memoizes what it resolves. Build a new instance per resolution rather than reusing one across changes to the env hash.

What lives here is Layer 1: where a store is, what it declares, and whether disk agrees. Turning that store into sessions is Layer 2 and lives in Enumeration, included below — the two halves met at 460 lines in one class and were split before Layer 3 readers could make it three.

Direct Known Subclasses

Amp, Claude, Codex, Copilot, Cursor, CursorIde, Gemini, Grok, Opencode, Pi, Qwen

Constant Summary collapse

FIDELITIES =
%i[full messages metadata unsupported].freeze

Constants included from Enumeration

Enumeration::MAX_LINE_BYTES

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Enumeration

#bytes_for, #encode_project, #project_dir_name, #project_path_for, #project_paths, #session_id_from, #sessions, #sessions_for_project, #started_at_for, #updated_at_for

Constructor Details

#initialize(env: ENV) ⇒ Base

Returns a new instance of Base.



77
78
79
# File 'lib/agent/sessions/adapters/base.rb', line 77

def initialize(env: ENV)
  @env = env
end

Class Attribute Details

.agent_nameObject (readonly)

Returns the value of attribute agent_name.



25
26
27
# File 'lib/agent/sessions/adapters/base.rb', line 25

def agent_name
  @agent_name
end

.declared_warningsObject (readonly)

Returns the value of attribute declared_warnings.



25
26
27
# File 'lib/agent/sessions/adapters/base.rb', line 25

def declared_warnings
  @declared_warnings
end

.documented_valueObject (readonly)

Returns the value of attribute documented_value.



25
26
27
# File 'lib/agent/sessions/adapters/base.rb', line 25

def documented_value
  @documented_value
end

.label_textObject (readonly)

Returns the value of attribute label_text.



25
26
27
# File 'lib/agent/sessions/adapters/base.rb', line 25

def label_text
  @label_text
end

.verified_on_dateObject (readonly)

Returns the value of attribute verified_on_date.



25
26
27
# File 'lib/agent/sessions/adapters/base.rb', line 25

def verified_on_date
  @verified_on_date
end

Class Method Details

.fidelity_valueObject

:unsupported is the honest default for an adapter that has not declared what a reader could reconstruct from its format.



29
# File 'lib/agent/sessions/adapters/base.rb', line 29

def fidelity_value = @fidelity_value || :unsupported

.homedir_configObject



31
# File 'lib/agent/sessions/adapters/base.rb', line 31

def homedir_config = @homedir_config || raise(Error, "#{inspect} declares no homedir")

.reader_classObject

The Layer 3 reader for this agent, or nil while it has none. nil is what makes Agent::Sessions.read raise UnsupportedFormat instead of handing back a reader that quietly yields nothing.



40
# File 'lib/agent/sessions/adapters/base.rb', line 40

def reader_class = nil

.store_configsObject



33
34
35
# File 'lib/agent/sessions/adapters/base.rb', line 33

def store_configs
  @store_configs || raise(Error, "#{inspect} declares no store")
end

Instance Method Details

#base_dirObject



133
134
135
136
137
138
139
# File 'lib/agent/sessions/adapters/base.rb', line 133

def base_dir
  @base_dir ||= begin
    config = self.class.homedir_config
    root = resolver.home(config[:name]).to_s
    config[:join] ? File.join(root, config[:join]) : root
  end
end

#locateObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/agent/sessions/adapters/base.rb', line 81

def locate
  Store.new(
    agent: self.class.agent_name,
    label: self.class.label_text,
    documented: self.class.documented_value,
    verified_on: self.class.verified_on_date,
    effective: layers.first,
    layers: layers,
    env_overrides: env_overrides,
    retention: retention,
    retention_source: retention_source,
    warnings: warnings
  )
end

#retentionObject



141
# File 'lib/agent/sessions/adapters/base.rb', line 141

def retention = nil

#retention_sourceObject



142
# File 'lib/agent/sessions/adapters/base.rb', line 142

def retention_source = :none

#verifyObject

Checks every declared store against disk. The design doc says each adapter declares its own checks, and each one does: its store_configs decide what is looked for and whether an absence is a failure or drift. Content-level checks (first record type, encoding round-trip) need file reads and wait for Layer 3. An adapter that needs its own can override this and call super.

The skip gate is the same signal Store#installed? uses: any declared store exists. It is deliberately NOT base-dir existence — ~/.cursor is created by the Cursor editor with no agent store in it (observed 2026-08-05), and the old gate made doctor report FAIL while where said "(not installed)". A missing store proves nothing on its own (never used? layout moved? the gem cannot tell), so :fail is reserved for the one case with evidence: some store exists, proving the agent records data here, while a required one is absent — the layout-moved signature.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/agent/sessions/adapters/base.rb', line 110

def verify
  unless layers.any?(&:exists?)
    detail = if Dir.exist?(base_dir)
               "#{base_dir} exists but holds none of the declared stores"
             else
               "#{base_dir} does not exist"
             end
    return [check(:skip, "agent is installed", detail)]
  end

  self.class.store_configs.map do |config|
    location = resolve(config)
    claim = "store #{config[:kind]} exists"
    if location.exists?
      check(:pass, claim, detail_for(location))
    elsif config[:optional]
      check(:drift, claim, "#{location.path} not found (optional; undocumented layouts drift)")
    else
      check(:fail, claim, "#{location.path} not found")
    end
  end
end

#warningsObject



144
145
146
# File 'lib/agent/sessions/adapters/base.rb', line 144

def warnings
  (self.class.declared_warnings || []).dup
end