Class: ClaudeAgentSDK::InMemorySessionStore

Inherits:
SessionStore show all
Defined in:
lib/claude_agent_sdk/session_store.rb

Overview

In-memory SessionStore for testing and development. Data is lost when the process exits — not suitable for production.

Instance Method Summary collapse

Methods inherited from SessionStore

implements?

Constructor Details

#initializeInMemorySessionStore

Returns a new instance of InMemorySessionStore.



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/claude_agent_sdk/session_store.rb', line 96

def initialize
  super
  @store = {}
  @mtimes = {}
  @summaries = {}
  @last_mtime = 0
  # The SessionStore#append contract requires per-key thread-safety, and two
  # concurrent sessions can share one store (each with its own batcher and
  # semaphore, so nothing serializes appends across them). Guard all access.
  @mutex = Mutex.new
end

Instance Method Details

#append(key, entries) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/claude_agent_sdk/session_store.rb', line 108

def append(key, entries)
  # Same guard as the reference adapters: append(key, []) must not create a
  # phantom key (load would return [] instead of nil and the session would
  # appear in listings).
  return if entries.nil? || entries.empty?

  @mutex.synchronize do
    k = key_to_string(key)
    (@store[k] ||= []).concat(entries)
    now_ms = next_mtime
    # Maintain the per-session summary sidecar incrementally so
    # #list_session_summaries never re-reads. Subagent subpaths don't
    # contribute to the main session's summary.
    if main_transcript_key?(key)
      sk = [key['project_key'], key['session_id']]
      folded = SessionSummary.fold_session_summary(@summaries[sk], key, entries)
      # Stamp with this adapter's storage write time — the SAME clock
      # #list_sessions exposes, so the fast-path staleness check works.
      folded['mtime'] = now_ms
      @summaries[sk] = folded
    end
    @mtimes[k] = now_ms
  end
  nil
end

#clearObject



214
215
216
217
218
219
220
221
# File 'lib/claude_agent_sdk/session_store.rb', line 214

def clear
  @mutex.synchronize do
    @store.clear
    @mtimes.clear
    @summaries.clear
    @last_mtime = 0
  end
end

#delete(key) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/claude_agent_sdk/session_store.rb', line 169

def delete(key)
  @mutex.synchronize do
    k = key_to_string(key)
    @store.delete(k)
    @mtimes.delete(k)
    # Deleting the main transcript cascades to its subkeys so they aren't
    # orphaned. A targeted delete with an explicit subpath removes only that
    # one entry. An empty-string subpath is treated as "no subpath" (main),
    # consistent with key_to_string / append, so it cascades like nil.
    next unless main_transcript_key?(key)

    @summaries.delete([key['project_key'], key['session_id']])
    prefix = "#{key['project_key']}/#{key['session_id']}/"
    @store.keys.select { |sk| sk.start_with?(prefix) }.each do |sk|
      @store.delete(sk)
      @mtimes.delete(sk)
    end
  end
  nil
end

#get_entries(key) ⇒ Object

All entries for a key (empty array if absent).



200
201
202
# File 'lib/claude_agent_sdk/session_store.rb', line 200

def get_entries(key)
  @mutex.synchronize { (@store[key_to_string(key)] || []).dup }
end

#list_session_summaries(project_key) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/claude_agent_sdk/session_store.rb', line 156

def list_session_summaries(project_key)
  @mutex.synchronize do
    # Return COPIES, not the internal summary objects: #load dups, so this
    # must too, or a caller mutating a returned summary's data would corrupt
    # the sidecar that the next fold builds on.
    @summaries.filter_map do |(pk, _sid), summary|
      next unless pk == project_key

      { 'session_id' => summary['session_id'], 'mtime' => summary['mtime'], 'data' => summary['data'].dup }
    end
  end
end

#list_sessions(project_key) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/claude_agent_sdk/session_store.rb', line 141

def list_sessions(project_key)
  prefix = "#{project_key}/"
  results = []
  @mutex.synchronize do
    @store.each_key do |k|
      next unless k.start_with?(prefix)

      rest = k[prefix.length..]
      # Only main transcripts (no subpath, so no second '/').
      results << { 'session_id' => rest, 'mtime' => @mtimes[k] || 0 } unless rest.include?('/')
    end
  end
  results
end

#list_subkeys(key) ⇒ Object



190
191
192
193
194
195
# File 'lib/claude_agent_sdk/session_store.rb', line 190

def list_subkeys(key)
  prefix = "#{key['project_key']}/#{key['session_id']}/"
  @mutex.synchronize do
    @store.keys.select { |k| k.start_with?(prefix) }.map { |k| k[prefix.length..] }
  end
end

#load(key) ⇒ Object



134
135
136
137
138
139
# File 'lib/claude_agent_sdk/session_store.rb', line 134

def load(key)
  @mutex.synchronize do
    entries = @store[key_to_string(key)]
    entries&.dup
  end
end

#sizeObject

Number of stored sessions (main transcripts only).



205
206
207
208
209
210
211
212
# File 'lib/claude_agent_sdk/session_store.rb', line 205

def size
  @mutex.synchronize do
    @store.keys.count do |k|
      first_slash = k.index('/')
      first_slash && !k[(first_slash + 1)..].include?('/')
    end
  end
end