ExAthena.Web.Sessions (ExAthena v0.17.0)

Copy Markdown View Source

File-based session persistence for the web UI.

Sessions are stored as Erlang term binaries under ~/.ex_athena/web/sessions/. A compact JSON index at ~/.ex_athena/web/recent.json tracks the most recently opened working directories so users can jump back to a project quickly.

Summary

Functions

Durably append a completed run's final assistant message to its session.

Delete a session file.

The chat message text for a finished run, surfacing the finish deliverable so the submitted answer is always visible in the main chat.

List all session headers, newest first.

List session headers for a specific working directory, newest first.

Return the list of recently opened working directories, newest first. Each entry: %{cwd: path, name: basename, opened_at: DateTime.t()}

Load a full session by id.

Build a run's final assistant message from the Result alone (no live UI state) and durably append it to the session. The durable path for when the process that owns the run wants the answer saved regardless of whether a LiveView is currently attached. Idempotent by msg_id (see append_run_result/4).

Remove a working directory from the recent list.

Persist a session. Overwrites if the id already exists.

Record a working directory as recently opened (deduplicates, caps at 20).

Functions

append_run_result(session_id, msg_id, assistant_msg, result)

@spec append_run_result(String.t(), String.t(), map(), map()) :: :ok

Durably append a completed run's final assistant message to its session.

Used by the run task so the answer survives even when the LiveView process that started the run has died (e.g. a websocket reconnect mid-run sends the final {:athena_done, result} to a now-dead pid). Load-modify-save, and idempotent by msg_id — a no-op if the LiveView already persisted that message, so the LiveView's richer (tool-event-bearing) version is preserved.

delete(id)

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

Delete a session file.

final_message_text(stream_text, arg2)

@spec final_message_text(String.t(), ExAthena.Result.t()) :: String.t()

The chat message text for a finished run, surfacing the finish deliverable so the submitted answer is always visible in the main chat.

When the run ended via finish (finish_reason: :submitted) with a deliverable: it becomes the message text if nothing was streamed (the orchestrator delegated and only submitted a deliverable), or is appended to the streamed text otherwise — unless that text already contains it. Any other termination keeps the streamed text unchanged.

list()

@spec list() :: [map()]

List all session headers, newest first.

list_for_cwd(cwd)

@spec list_for_cwd(String.t()) :: [map()]

List session headers for a specific working directory, newest first.

list_recent()

@spec list_recent() :: [map()]

Return the list of recently opened working directories, newest first. Each entry: %{cwd: path, name: basename, opened_at: DateTime.t()}

load(id)

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

Load a full session by id.

persist_run_result(session_id, msg_id, result)

@spec persist_run_result(String.t(), String.t(), ExAthena.Result.t()) :: :ok

Build a run's final assistant message from the Result alone (no live UI state) and durably append it to the session. The durable path for when the process that owns the run wants the answer saved regardless of whether a LiveView is currently attached. Idempotent by msg_id (see append_run_result/4).

remove_recent(cwd)

@spec remove_recent(String.t()) :: :ok

Remove a working directory from the recent list.

save(data)

@spec save(map()) :: :ok

Persist a session. Overwrites if the id already exists.

touch_recent(cwd)

@spec touch_recent(String.t()) :: :ok

Record a working directory as recently opened (deduplicates, caps at 20).