ExAthena.Session (ExAthena v0.4.0)

Copy Markdown View Source

GenServer that owns a multi-turn conversation.

A Session is the right abstraction when you want:

  • the message history to persist across multiple user turns,
  • resumable state across LiveView reconnects,
  • streaming deltas broadcast to subscribers,
  • an identifiable process pid / name you can monitor.

For one-shot agent runs, use ExAthena.Loop.run/2 directly. For truly stateless single-turn inference, ExAthena.query/2.

Usage

{:ok, pid} = ExAthena.Session.start_link(
  provider: :ollama,
  model: "llama3.1",
  tools: :all,
  cwd: "/path/to/project"
)

{:ok, result} = ExAthena.Session.send_message(pid, "read mix.exs and list deps")
IO.puts(result.text)

ExAthena.Session.stop(pid)

Each send_message appends to the session's message list, runs the agent loop to completion, and returns the final result. Subsequent messages include the full prior history, so the model has context.

Summary

Functions

Returns a specification to start this module under a supervisor.

Return the current message list (for debugging / persistence).

Resume a session by reading prior events back from a store.

Send a user message; blocks until the loop terminates.

Return the stable session id assigned at start.

Start a session. Accepts the same options as ExAthena.Loop.run/2 plus

Stop the session.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

messages(server)

@spec messages(GenServer.server()) :: [map()]

Return the current message list (for debugging / persistence).

resume(session_id, opts \\ [])

@spec resume(
  String.t(),
  keyword()
) :: {:ok, [map()]} | {:error, term()}

Resume a session by reading prior events back from a store.

Returns the reconstructed message list. Permissions deliberately do NOT survive resume — Claude Code's design pattern: each session re-establishes trust with the host. The caller is expected to feed the returned messages into a fresh start_link/1 call via :messages.

send_message(server, message, opts \\ [])

@spec send_message(GenServer.server(), String.t(), keyword()) ::
  {:ok, map()} | {:error, term()}

Send a user message; blocks until the loop terminates.

session_id(server)

@spec session_id(GenServer.server()) :: String.t()

Return the stable session id assigned at start.

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Start a session. Accepts the same options as ExAthena.Loop.run/2 plus:

  • :name — GenServer name.
  • :system_prompt — pinned system prompt used on every turn.
  • :store:in_memory (default), :jsonl, or a custom module implementing ExAthena.Sessions.Store. Per-turn events are persisted via the chosen store; resume/2 reads them back.

stop(server)

@spec stop(GenServer.server()) :: :ok

Stop the session.