Unified session history store for the BeamAgent SDK.
This module provides ETS-backed session tracking and message history across all five agentic coder backends (Claude, Codex, Gemini, OpenCode, Copilot). Every adapter records messages here regardless of whether the underlying CLI has native session history support, giving callers a single consistent interface for session management.
When to use directly vs through BeamAgent
Most callers interact with sessions through BeamAgent. Use this module directly
when you need fine-grained control over session metadata, message querying,
forking, sharing, or summarization — for example, in a custom session supervisor
or an audit trail consumer.
Quick example
# List all sessions sorted by most-recently updated:
{:ok, sessions} = BeamAgent.SessionStore.list_sessions()
# Filter to recent Claude sessions:
{:ok, recent} = BeamAgent.SessionStore.list_sessions(%{
adapter: :claude,
limit: 10,
since: System.os_time(:millisecond) - 3_600_000
})
# Fetch messages for a specific session:
{:ok, messages} = BeamAgent.SessionStore.get_session_messages("sess_abc123")
# Fork a session for safe experimentation:
{:ok, fork} = BeamAgent.SessionStore.fork_session("sess_abc123", %{})
IO.inspect(fork.session_id)Core concepts
Session metadata: each session is identified by a binary session ID and carries metadata such as the adapter name, model, working directory, timestamps, and a message count.
Message recording: messages are stored with auto-incrementing sequence numbers that preserve insertion order and enable efficient per-session queries.
Forking:
fork_session/2creates a deep copy of a session (metadata and all messages) under a new session ID, recording the parent relationship in theextra.forkfield.Sharing:
share_session/1generates a share ID that marks the session as externally visible.unshare_session/1revokes access.Reverting:
revert_session/2hides messages beyond a boundary without deleting them;unrevert_session/1restores the full view.
Architecture deep dive
This module is a thin Elixir facade that defdelegates every call to the Erlang
:beam_agent_session_store module. The underlying ETS tables
(beam_agent_sessions, beam_agent_session_messages, beam_agent_session_counters)
are public and named so any process can read and write without bottlenecking on a
single owner. Tables are created lazily on first access and persist for the
lifetime of the BEAM node.
See also: BeamAgent.Threads, BeamAgent.Checkpoint, BeamAgent.
Summary
Types
Active share state returned by share_session/1 and share_session/2.
Options for filtering session listings.
A session message record with a required :type key and optional wire fields.
Options for querying session messages.
Session metadata map.
Share state for a session.
Summary of a session's conversation history.
Functions
Delete a session and all its messages.
Fork (deep copy) an existing session.
Get messages from the backend's native session store.
Get messages from the backend's native session store with options.
Get metadata for a specific session.
Get all messages for a session in recording order.
Get messages for a session with filtering options.
List sessions from the backend's native session store.
List sessions from the backend's native session store with filters.
List all sessions in the store, sorted by most-recently updated.
List sessions with optional filters.
Revert the visible conversation to a prior message boundary.
Generate a share token for a session.
Generate a share token for a session with options.
Generate and store a summary for a session.
Generate and store a summary for a session with options.
Clear revert state and restore the full visible history.
Revoke the current share for a session.
Types
@type list_opts() :: %{ optional(:adapter) => atom(), optional(:cwd) => binary(), optional(:model) => binary(), optional(:limit) => pos_integer(), optional(:since) => integer() }
Options for filtering session listings.
Supported keys: adapter (atom), cwd (binary), model (binary),
limit (pos_integer), since (unix millisecond timestamp).
@type message() :: %{ :type => atom(), optional(:content) => binary(), optional(:tool_name) => binary(), optional(:tool_input) => map(), optional(:raw) => map(), optional(:timestamp) => integer(), optional(:uuid) => binary(), optional(:session_id) => binary(), optional(:content_blocks) => [map()], optional(:parent_tool_use_id) => binary() | nil, optional(:tool_use_id) => binary(), optional(:message_id) => binary(), optional(:model) => binary(), optional(:duration_ms) => non_neg_integer(), optional(:duration_api_ms) => non_neg_integer(), optional(:error_info) => map() }
A session message record with a required :type key and optional wire fields.
@type message_opts() :: %{ optional(:limit) => pos_integer(), optional(:offset) => non_neg_integer(), optional(:types) => [atom()], optional(:include_hidden) => boolean() }
Options for querying session messages.
Supported keys: limit (pos_integer), offset (non_neg_integer),
types (list of message type atoms), include_hidden (boolean).
@type session_meta() :: %{ :session_id => binary(), optional(:adapter) => atom(), optional(:model) => binary(), optional(:cwd) => binary(), optional(:created_at) => integer(), optional(:updated_at) => integer(), optional(:message_count) => non_neg_integer(), optional(:extra) => map() }
Session metadata map.
Contains the session ID, adapter name, model, working directory, creation and
update timestamps, message count, and an optional extra map for fork/share/
summary/view state.
@type session_summary() :: %{ session_id: binary(), content: binary(), generated_at: integer(), message_count: non_neg_integer(), generated_by: binary() }
Summary of a session's conversation history.
Contains the session ID, generated content, generation timestamp, message count at generation time, and the generator identifier.
Functions
@spec delete_session(binary()) :: :ok
Delete a session and all its messages.
Removes the session metadata, message counter, and every recorded message for
this session from the store. Also fires a completion event via
:beam_agent_events.
@spec fork_session(binary(), map()) :: {:ok, session_meta()} | {:error, :not_found}
Fork (deep copy) an existing session.
Creates a new session with copies of all metadata and messages from the source
session. The new session records its lineage in the extra.fork field
(parent_session_id, forked_at).
opts may include:
:session_id— explicit ID for the fork (auto-generated if omitted):include_hidden— whether to copy hidden (reverted) messages (defaulttrue):extra— additional metadata to merge into the fork
Returns {:ok, fork_meta} with the new session metadata, or
{:error, :not_found} if the source session does not exist.
Example
{:ok, fork} = BeamAgent.SessionStore.fork_session("sess_001", %{
session_id: "sess_fork_001"
})
fork.session_id # => "sess_fork_001"
Get messages from the backend's native session store.
Falls back to get_session_messages/1 if native message retrieval is
not supported by the backend.
Parameters
session_id-- binary session identifier.
Returns
{:ok, messages}or{:error, reason}.
Get messages from the backend's native session store with options.
Falls back to get_session_messages/2 if native retrieval is not supported.
Parameters
session_id-- binary session identifier.opts-- backend-specific message filter options.
Returns
{:ok, messages}or{:error, reason}.
@spec get_session(binary()) :: {:ok, session_meta()} | {:error, :not_found}
Get metadata for a specific session.
Returns {:ok, meta} with the session metadata map, or
{:error, :not_found} if no session with this ID exists.
Example
{:ok, meta} = BeamAgent.SessionStore.get_session("sess_001")
meta.model
Get all messages for a session in recording order.
Returns {:ok, messages} or {:error, :not_found}.
@spec get_session_messages(binary(), message_opts()) :: {:ok, [message()]} | {:error, :not_found}
Get messages for a session with filtering options.
opts may include:
:limit— maximum number of messages (pos_integer):offset— messages to skip from the start (non_neg_integer):types— only include messages of these types (list of atoms):include_hidden— iftrue, include messages hidden by revert (boolean)
Returns {:ok, messages} or {:error, :not_found}.
@spec list_native_sessions() :: {:ok, [session_meta()]} | {:error, term()}
List sessions from the backend's native session store.
Attempts to call the backend's native session listing. Falls back to
list_sessions/0 if the backend does not support native session listing.
Returns
{:ok, sessions}or{:error, reason}.
@spec list_native_sessions(map()) :: {:ok, [session_meta()]} | {:error, term()}
List sessions from the backend's native session store with filters.
Like list_native_sessions/0 but passes filter options to the native call.
Falls back to list_sessions/1 if native listing is not supported.
Parameters
opts-- backend-specific filter options map.
Returns
{:ok, sessions}or{:error, reason}.
@spec list_sessions() :: {:ok, [session_meta()]}
List all sessions in the store, sorted by most-recently updated.
Returns {:ok, sessions} with all session metadata maps. Equivalent to
calling list_sessions(%{}).
Example
iex> {:ok, sessions} = BeamAgent.SessionStore.list_sessions()
iex> is_list(sessions)
true
@spec list_sessions(list_opts()) :: {:ok, [session_meta()]}
List sessions with optional filters.
Returns sessions matching all provided filter criteria, sorted by updated_at
descending.
opts is a map with optional keys:
:adapter— only sessions using this adapter (atom):cwd— only sessions with this working directory (binary):model— only sessions using this model (binary):limit— maximum number of results (pos_integer):since— only sessions updated at or after this timestamp (unix ms)
Example
{:ok, recent} = BeamAgent.SessionStore.list_sessions(%{
adapter: :claude,
limit: 5
})
@spec revert_session(binary(), map()) :: {:ok, session_meta()} | {:error, :invalid_selector | :not_found}
Revert the visible conversation to a prior message boundary.
Hides messages beyond the specified boundary without deleting them. The
underlying message store remains append-only; revert changes the active view by
storing a visible_message_count in the session extra.view field.
selector is a map with one of:
:visible_message_count— set the boundary to exactly N messages:message_idor:uuid— set the boundary to a specific message
Returns {:ok, updated_meta}, {:error, :not_found}, or
{:error, :invalid_selector}.
@spec summarize_session(binary()) :: {:ok, session_summary()} | {:error, :not_found}
Generate and store a summary for a session.
Builds a deterministic text summary from the session message history and stores
it in the session extra.summary field. Equivalent to calling
summarize_session/2 with an empty opts map.
Returns {:ok, summary} or {:error, :not_found}.
@spec summarize_session(binary(), map()) :: {:ok, session_summary()} | {:error, :not_found}
Generate and store a summary for a session with options.
opts may include:
:contentor:summary— explicit summary text (auto-derived if omitted):generated_by— identifier for the summary generator
Returns {:ok, summary} or {:error, :not_found}.
@spec unrevert_session(binary()) :: {:ok, session_meta()} | {:error, :not_found}
Clear revert state and restore the full visible history.
Removes the visible_message_count boundary from the session view, making all
recorded messages visible again.
Returns {:ok, updated_meta} or {:error, :not_found}.