MingaAgent.SessionStore (Minga v0.1.0)

Copy Markdown View Source

Persists agent conversations to disk as JSON files.

Each session is saved as {session_id}.json in the sessions directory (~/.config/minga/agent/sessions/ by default). Files are written atomically via a temp file + rename to avoid corruption on crash. Manager-owned remote identity is stored separately under .remote_tokens/.

The store is stateless: all functions operate directly on the filesystem. The Session GenServer calls save/2 on a debounced timer, and the picker calls list/0 to scan the directory for past sessions.

Summary

Types

Full session data for save/load.

Session metadata for the picker (without full message content).

Functions

Deletes all saved transcripts before explicitly dropping each artifact record.

Durably deletes a transcript before explicitly dropping its retained artifact record.

Establishes manager-owned remote session identity.

Lists all saved sessions as metadata (without full messages).

Loads a versioned, lossless session snapshot.

Loads a session and reconciles retained snapshot pin generations.

Reads an unversioned or version-one display-only record for explicit one-way import.

Prunes session transcripts older than days days.

Returns the sessions directory path.

Types

session_data()

@type session_data() :: %{
  :id => String.t(),
  :timestamp => String.t(),
  :model_name => String.t(),
  :messages => [MingaAgent.Message.t()],
  :usage => MingaAgent.TurnUsage.t(),
  :continuation => MingaAgent.Session.Continuation.t(),
  optional(:model_selection) =>
    MingaAgent.ModelSelection.t() | MingaAgent.ModelSelection.Stored.t() | nil,
  optional(:selection_intent) => map(),
  optional(:last_message_at) => String.t(),
  optional(:title) => String.t(),
  optional(:provider_name) => String.t(),
  optional(:branches) => [MingaAgent.Branch.t()],
  optional(:message_ids) => [pos_integer()],
  optional(:pinned_ids) => MapSet.t(pos_integer()),
  optional(:memory) => String.t() | nil
}

Full session data for save/load.

session_meta()

@type session_meta() :: %{
  id: String.t(),
  timestamp: String.t(),
  last_message_at: String.t(),
  title: String.t(),
  model_name: String.t(),
  provider_name: String.t(),
  preview: String.t(),
  recent_messages: String.t(),
  message_count: non_neg_integer(),
  turn_count: non_neg_integer(),
  cost: float(),
  continuation_kind: :lossless | :legacy_reconstructed | :legacy_import_required
}

Session metadata for the picker (without full message content).

Functions

clear_all(config_dir \\ nil, opts \\ [])

@spec clear_all(String.t() | nil, keyword()) :: :ok

Deletes all saved transcripts before explicitly dropping each artifact record.

delete(session_id, config_dir \\ nil, opts \\ [])

@spec delete(String.t(), String.t() | nil, keyword()) :: :ok | {:error, term()}

Durably deletes a transcript before explicitly dropping its retained artifact record.

establish_remote_token(session_id, candidate, config_dir \\ nil)

@spec establish_remote_token(String.t(), String.t(), String.t() | nil) ::
  {:ok, String.t()} | {:error, term()}

Establishes manager-owned remote session identity.

Existing canonical identity wins over legacy transcript identity and the candidate. A new identity is persisted before it is returned.

list(config_dir \\ nil)

@spec list(String.t() | nil) :: [session_meta()]

Lists all saved sessions as metadata (without full messages).

Returns sessions sorted by last message timestamp, most recent first.

load(session_id, config_dir \\ nil)

@spec load(String.t(), String.t() | nil) :: {:ok, session_data()} | {:error, term()}

Loads a versioned, lossless session snapshot.

Legacy display-only records are rejected until the caller explicitly uses load_legacy/2; unknown future versions are never guessed.

load(session_id, config_dir, opts)

@spec load(String.t(), String.t() | nil, keyword()) ::
  {:ok, session_data()} | {:error, term()}

Loads a session and reconciles retained snapshot pin generations.

load_legacy(session_id, config_dir \\ nil)

@spec load_legacy(String.t(), String.t() | nil) ::
  {:ok, session_data()} | {:error, term()}

Reads an unversioned or version-one display-only record for explicit one-way import.

prune(days, config_dir \\ nil, opts \\ [])

@spec prune(non_neg_integer(), String.t() | nil, keyword()) :: non_neg_integer()

Prunes session transcripts older than days days.

Returns the number durably removed before their artifact records were explicitly dropped. Durable remote identities are retained.

save(data, config_dir \\ nil, opts \\ [])

@spec save(session_data(), String.t() | nil, keyword()) :: :ok | {:error, term()}

Saves a session to disk.

Candidate artifact references are pinned before the private temporary file is written. The prior snapshot pin is released only after rename and parent directory synchronization make the candidate durable.

sessions_dir(config_dir \\ nil)

@spec sessions_dir(String.t() | nil) :: String.t()

Returns the sessions directory path.