Jido.Agent.InstanceManager (Jido v2.0.0-rc.1)

View Source

Keyed singleton registry with lifecycle management and optional persistence.

InstanceManager provides a pattern for managing one agent per logical context (user session, game room, connection, conversation). This is NOT a pool—each key maps to exactly one agent instance. Features:

  • Keyed singletons — one agent per key, lookup or start on demand
  • Automatic lifecycle — idle timeout with attachment tracking
  • Optional persistence — hibernate/thaw with pluggable storage backends
  • Multiple registries — different agent types, different configurations

Architecture

Each instance manager consists of:

Usage

# In your supervision tree
children = [
  Jido.Agent.InstanceManager.child_spec(
    name: :sessions,
    agent: MyApp.SessionAgent,
    idle_timeout: :timer.minutes(15),
    persistence: [
      store: {Jido.Agent.Store.ETS, table: :session_cache}
    ]
  )
]

# At runtime
{:ok, pid} = Jido.Agent.InstanceManager.get(:sessions, "user-123")
:ok = Jido.AgentServer.attach(pid)  # Track this caller as attached

Options

  • :name - Instance manager name (required, atom)
  • :agent - Agent module (required)
  • :idle_timeout - Time in ms before idle agent hibernates/stops (default: :infinity)
  • :persistence - Persistence configuration (optional)
    • :store - {StoreModule, opts} tuple
    • :key_fun - Custom key function (agent_module, agent_id) -> key
  • :agent_opts - Additional options passed to AgentServer

Lifecycle

  1. get/3 looks up by key in Registry
  2. If not found and persistence enabled, tries to thaw from store
  3. If still not found, starts fresh agent
  4. Callers use attach/1 to track interest
  5. When all attachments gone, idle timer starts
  6. On idle timeout: hibernate to store (if configured) then stop

Phoenix Integration

# LiveView mount
def mount(_params, %{"session_key" => key}, socket) do
  if connected?(socket) do
    {:ok, pid} = Jido.Agent.InstanceManager.get(:sessions, key)
    :ok = Jido.AgentServer.attach(pid)
    {:ok, assign(socket, agent_pid: pid)}
  else
    {:ok, socket}
  end
end

Summary

Functions

Returns a child specification for starting an instance manager under a supervisor.

Gets or starts an agent by key.

Looks up an agent by key without starting.

Returns statistics for an instance manager.

Stops an agent by key.

Types

key()

@type key() :: term()

manager_name()

@type manager_name() :: atom()

Functions

child_spec(init_arg)

@spec child_spec(keyword()) :: Supervisor.child_spec()

Returns a child specification for starting an instance manager under a supervisor.

Options

  • :name - Instance manager name (required)
  • :agent - Agent module (required)
  • :idle_timeout - Idle timeout in ms (default: :infinity)
  • :persistence - [store: {Module, opts}] (optional)
  • :agent_opts - Options passed to AgentServer (optional)

Examples

Jido.Agent.InstanceManager.child_spec(
  name: :sessions,
  agent: MyApp.SessionAgent,
  idle_timeout: :timer.minutes(15)
)

get(manager, key, opts \\ [])

@spec get(manager_name(), key(), keyword()) :: {:ok, pid()} | {:error, term()}

Gets or starts an agent by key.

If an agent for the given key is already running, returns its pid. If persistence is configured and a hibernated state exists, thaws it. Otherwise starts a fresh agent.

Options

  • :initial_state - Initial state for fresh agents (default: %{})

Examples

{:ok, pid} = Jido.Agent.InstanceManager.get(:sessions, "user-123")
{:ok, pid} = Jido.Agent.InstanceManager.get(:sessions, "user-123", initial_state: %{foo: 1})

lookup(manager, key)

@spec lookup(manager_name(), key()) :: {:ok, pid()} | :error

Looks up an agent by key without starting.

Examples

{:ok, pid} = Jido.Agent.InstanceManager.lookup(:sessions, "user-123")
:error = Jido.Agent.InstanceManager.lookup(:sessions, "nonexistent")

stats(manager)

@spec stats(manager_name()) :: %{count: non_neg_integer(), keys: [key()]}

Returns statistics for an instance manager.

Examples

%{count: 5, keys: [...]} = Jido.Agent.InstanceManager.stats(:sessions)

stop(manager, key)

@spec stop(manager_name(), key()) :: :ok | {:error, :not_found}

Stops an agent by key.

If persistence is configured, the agent will hibernate before stopping. Uses a graceful shutdown to ensure the agent's terminate callback runs.

Examples

:ok = Jido.Agent.InstanceManager.stop(:sessions, "user-123")
{:error, :not_found} = Jido.Agent.InstanceManager.stop(:sessions, "nonexistent")