defmodule Pi.Session.State do @moduledoc "Semantic state owned by a Pi session process." alias Pi.Agent.Messages alias Pi.Protocol.LLM.Message alias Pi.Session.Event @enforce_keys [:id] defstruct [ :id, :parent_id, :name, :system, :status, :result, :error, started_at: nil, updated_at: nil, messages: [], events: [], metadata: %{} ] @type status :: :idle | :running | :done | :failed | :cancelled @type t :: %__MODULE__{ id: String.t(), parent_id: String.t() | nil, name: atom() | String.t() | nil, system: String.t() | nil, status: status(), result: term(), error: term(), started_at: DateTime.t() | nil, updated_at: DateTime.t() | nil, messages: [Message.t()], events: [Event.t()], metadata: map() } def new(opts \\ []) do now = DateTime.utc_now() %__MODULE__{ id: Keyword.get_lazy(opts, :id, &id/0), parent_id: Keyword.get(opts, :parent_id), name: Keyword.get(opts, :name), system: Keyword.get(opts, :system), status: Keyword.get(opts, :status, :idle), started_at: now, updated_at: now, messages: opts |> Keyword.get(:messages, []) |> Enum.map(&Messages.normalize/1), metadata: Keyword.get(opts, :metadata, %{}) } end def child(%__MODULE__{} = parent, opts \\ []) do opts |> Keyword.put_new(:parent_id, parent.id) |> new() end defp id, do: "session_#{System.unique_integer([:positive])}" end