defmodule <%= module %> do @moduledoc """ Implements `Sagents.AgentPersistence` for state snapshots. Persists full agent state (messages, todos, metadata) to the database via `<%= conversations_module %>.save_agent_state/3`, and mirrors the durable interrupt flag onto `conversation.metadata["interrupted"]` via `set_interrupted/3` (called by sagents only on actual transitions). """ @behaviour Sagents.AgentPersistence require Logger @impl true def persist_state(scope, state_data, context) do conversation_id = extract_conversation_id(context.agent_id) case <%= conversations_module %>.save_agent_state(scope, conversation_id, state_data) do {:ok, _agent_state} -> Logger.debug("Persisted agent state for #{context.agent_id} (#{context.lifecycle})") :ok {:error, %Ecto.Changeset{errors: errors}} = error -> if conversation_deleted?(errors) do Logger.warning( "Skipping agent state persistence for #{context.agent_id} (#{context.lifecycle}): conversation no longer exists" ) :ok else error end {:error, :not_found} -> Logger.warning( "Skipping agent state persistence for #{context.agent_id} (#{context.lifecycle}): conversation not accessible in scope" ) :ok end end @impl true def load_state(scope, context) do conversation_id = extract_conversation_id(context.agent_id) <%= conversations_module %>.load_agent_state(scope, conversation_id) end @impl true def set_interrupted(scope, context, interrupted?) do conversation_id = extract_conversation_id(context.agent_id) case <%= conversations_module %>.set_interrupt_status(scope, conversation_id, interrupted?) do {:ok, _conversation} -> :ok {:error, :not_found} -> Logger.warning( "Skipping interrupt flag update for #{context.agent_id}: conversation not accessible in scope" ) :ok other -> Logger.warning( "Failed to update interrupt flag for #{context.agent_id}: #{inspect(other)}" ) :ok end end defp extract_conversation_id(agent_id) do String.replace_prefix(agent_id, "conversation-", "") end defp conversation_deleted?(changeset_errors) do Enum.any?(changeset_errors, fn {:conversation_id, {_msg, opts}} -> Keyword.get(opts, :constraint) == :foreign _other -> false end) end end