Class: ClaudeAgentSDK::SessionStore

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

Overview

Adapter for mirroring session transcripts to external storage.

The subprocess still writes to local disk; the adapter receives a secondary copy via SessionStore#append, and resume can materialize from the store via SessionStore#load when the local file is absent.

Only #append and #load are required. The remaining methods are optional: implementers may omit them, and the SDK probes for their presence via SessionStore.implements? before invoking (it never uses is_a? for this — a duck-typed adapter need not subclass SessionStore). The default implementations here raise NotImplementedError so subclasses inherit them as "absent" markers.

All keys/entries cross the adapter boundary as Hashes with STRING keys:

- SessionKey: { 'project_key' => String, 'session_id' => String,
              'subpath' => String (optional; omit for the main transcript) }
- entries: raw JSONL transcript objects (opaque pass-through blobs)
- list_sessions result: [{ 'session_id' => String, 'mtime' => Integer }]
- summary entries: { 'session_id', 'mtime', 'data' } (see SessionSummary)

FIBER-NATIVE ADAPTERS (issue #47 phase 3): an adapter whose IO is entirely Fiber-scheduler-aware may additionally define an optional callback_scheduling method returning :inline (:thread = default behavior). The SDK then runs the adapter's timeout-bounded calls in place on the reactor fiber under a cooperative timeout instead of on a throwaway thread with a hard Thread#join bound. The declaration covers EVERY method the SDK invokes on the adapter — append, load, list_sessions, list_session_summaries, list_subkeys — not just append/load: resume materialization inlines the listing calls too. Only the adapter author can make this call — declare :inline ONLY if every blocking operation in every method yields to the scheduler; scheduler-opaque blocking stalls every job on the worker AND escapes the cooperative deadline. A timed-out inline call is interrupted at its next suspension point (ensure blocks run) rather than abandoned. The cancellation reaches only the adapter's own fiber — work the adapter offloaded (descendant tasks, an already-issued remote write) may still land afterwards — so timed-out appends are NOT retried (same as thread mode) and a cancelled append may remain permanently half-applied in the store. The drop is surfaced (MirrorErrorMessage, batches_dropped?) and the local transcript remains the source of truth; the dedupe-by-entry-uuid recommendation above stays advisory. The method is deliberately NOT defined here: the SDK probes respond_to?(:callback_scheduling) (see SessionStores.store_callback_scheduling), so pure duck-typed adapters stay minimal, and an app can opt a third-party fiber-native adapter in via a singleton method (def store.callback_scheduling = :inline).

Direct Known Subclasses

InMemorySessionStore

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.implements?(store, method) ⇒ Boolean

True if store overrides method rather than inheriting the base implementation that raises NotImplementedError. Works for both subclasses and duck-typed adapters (whose method owner is their own class).

Returns:

  • (Boolean)


65
66
67
68
69
70
71
# File 'lib/claude_agent_sdk/session_store.rb', line 65

def self.implements?(store, method)
  return false unless store.respond_to?(method)

  store.method(method).owner != SessionStore
rescue NameError
  false
end

Instance Method Details

#append(_key, _entries) ⇒ Object

Mirror a batch of transcript entries. Called AFTER the subprocess's local write succeeds. Required.

Appends for a given key are normally serialized by the batcher, but if an #append exceeds the send timeout the batcher abandons that (still-running) call and proceeds, so a later #append for the SAME key can overlap it. Implementations must therefore be thread-safe per key (a per-call connection — Postgres/Redis/etc. — satisfies this) and should dedupe by entry when present, since a retried/overlapping batch may repeat a prior write.

Raises:

  • (NotImplementedError)


83
84
85
# File 'lib/claude_agent_sdk/session_store.rb', line 83

def append(_key, _entries)
  raise NotImplementedError, "#{self.class} must implement #append"
end

#delete(_key) ⇒ Object

Delete a session. Deleting a main-transcript key (no subpath) must cascade to all subkeys. Optional — if unimplemented, deletion is a no-op.

Raises:

  • (NotImplementedError)


108
109
110
# File 'lib/claude_agent_sdk/session_store.rb', line 108

def delete(_key)
  raise NotImplementedError
end

#list_session_summaries(_project_key) ⇒ Object

Return incrementally-maintained summaries for all sessions in one call. Optional — if unimplemented, list_sessions_from_store falls back to list_sessions + per-session load.

Raises:

  • (NotImplementedError)


102
103
104
# File 'lib/claude_agent_sdk/session_store.rb', line 102

def list_session_summaries(_project_key)
  raise NotImplementedError
end

#list_sessions(_project_key) ⇒ Object

List sessions for a project_key as [{ 'session_id', 'mtime' }]. Optional — if unimplemented, list_sessions_from_store raises.

Raises:

  • (NotImplementedError)


95
96
97
# File 'lib/claude_agent_sdk/session_store.rb', line 95

def list_sessions(_project_key)
  raise NotImplementedError
end

#list_subkeys(_key) ⇒ Object

List all subpath keys under a session (e.g. subagent transcripts). Optional — if unimplemented, resume only materializes the main transcript.

Raises:

  • (NotImplementedError)


114
115
116
# File 'lib/claude_agent_sdk/session_store.rb', line 114

def list_subkeys(_key)
  raise NotImplementedError
end

#load(_key) ⇒ Object

Load a full session for resume, or nil for a key that was never written. Required.

Raises:

  • (NotImplementedError)


89
90
91
# File 'lib/claude_agent_sdk/session_store.rb', line 89

def load(_key)
  raise NotImplementedError, "#{self.class} must implement #load"
end