# `Jido.AgentServer`
[🔗](https://github.com/agentjido/jido/blob/v2.3.0/lib/jido/agent_server.ex#L1)

GenServer runtime for Jido agents.

AgentServer is the "Act" side of the Jido framework: while Agents "think"
(pure decision logic via `cmd/2`), AgentServer "acts" by executing the
directives they emit. Signal routing happens in AgentServer, keeping
Agents purely action-oriented.

## Architecture

- Single GenServer per agent under `Jido.AgentSupervisor`
- Internal directive queue with drain loop for non-blocking processing
- Registry-based naming via `Jido.Registry`
- Logical parent-child hierarchy via state tracking + monitors

Jido's parent-child hierarchy is **logical**, not OTP supervisory ancestry.
Parent and child agents are still OTP peers under a supervisor; the parent
relationship is represented explicitly with `Jido.AgentServer.ParentRef`,
runtime monitors, and lifecycle signals.

## Public API

- `start/1` - Start under DynamicSupervisor
- `start_link/1` - Start linked to caller
- `call/3` - Synchronous signal processing
- `cast/2` - Asynchronous signal processing
- `state/1` - Get full State struct
- `whereis/1` - Registry lookup by ID (default registry)
- `whereis/2` - Registry lookup by ID (specific registry)

## Signal Flow

 ```
 Signal → AgentServer.call/cast
       → plugin handle_signal/2
       → plugin prepare_signal/2
       → route_signal_to_action (via strategy/agent/plugin routes)
       → plugin prepare_action/3
       → Agent.cmd/3
       → {agent, directives}
       → directives queued with prepared signal and runtime context
       → drain loop executes via DirectiveExec protocol
       → plugin prepare_emit/2 for emitted signals
       → dispatch
       → transform_result/3 on synchronous call return only
 ```

 Signal routing is owned by AgentServer, not the Agent. Strategies can define
`signal_routes/1` to map signal types to strategy commands. Unmatched signals
fall back to `{signal.type, signal.data}` as the action.

## Options

- `:jido` - Jido instance name for registry scoping (default: `Jido`)
- `:agent` - Agent module or struct (required)
- `:id` - Instance ID (auto-generated if not provided)
- `:initial_state` - Initial state map for agent
- `:registry` - Registry module (default: `Jido.Registry`)
- `:default_dispatch` - Default dispatch config for Emit directives (fallback: current agent pid)
- `:error_policy` - Error handling policy
- `:max_queue_size` - Max directive queue size (default: 10_000)
- `:parent` - Parent reference for hierarchy
- `:on_parent_death` - Behavior when parent dies:
  - `:stop` - stop the child
  - `:continue` - keep running and become orphaned
  - `:emit_orphan` - become orphaned and process `jido.agent.orphaned`
- `:spawn_fun` - Custom function for spawning children
- `:debug` - Enable debug mode with event buffer (default: `false`)

## Agent Resolution

The `:agent` option accepts:

- **Module name** - Must implement `new/0` or `new/1`
  - `new/1` receives `[id: id, state: initial_state]` as keyword options
  - `new/0` creates agent with defaults; `:id` and `:initial_state` options are ignored
- **Agent struct** - Used directly
  - Provide `:agent_module` option to specify the module if it differs from `agent.__struct__`
  - The struct's ID takes precedence over the `:id` option

The `:agent_module` option is only used when `:agent` is a struct. It tells AgentServer which module implements the agent behavior (for calling `cmd/2`, lifecycle hooks, etc.).

## Examples

    # Using global Jido instance (default)
    {:ok, pid} = AgentServer.start_link(agent: SimpleAgent)

    # Using a named Jido instance
    {:ok, pid} = AgentServer.start_link(jido: MyApp.Jido, agent: MyAgent)

    # Module with new/1 - receives id and state
    {:ok, pid} = AgentServer.start_link(
      agent: MyAgent,
      id: "my-id",
      initial_state: %{counter: 42}
    )

    # Pre-built struct - requires agent_module
    agent = MyAgent.new(id: "prebuilt", state: %{value: 99})
    {:ok, pid} = AgentServer.start_link(agent: agent, agent_module: MyAgent)

## Completion Detection

Agents signal completion via **state**, not process death:

    # In your strategy/agent, set terminal status:
    agent = put_in(agent.state.status, :completed)
    agent = put_in(agent.state.last_answer, answer)

    # External code polls for completion:
    {:ok, state} = AgentServer.state(server)
    case state.agent.state.status do
      :completed -> state.agent.state.last_answer
      :failed -> {:error, state.agent.state.error}
      _ -> :still_running
    end

This follows Elm/Redux semantics where completion is a state concern.
The process stays alive until explicitly stopped or supervised.

**Do NOT** use `{:stop, ...}` from DirectiveExec for normal completion—this
causes race conditions with async work and skips lifecycle hooks.
See `Jido.AgentServer.DirectiveExec` for details.

## Debugging

AgentServer can record recent events in an in-memory ring buffer (max 50)
to help diagnose what happened inside a running agent.

Enable at start:

    {:ok, pid} = AgentServer.start_link(agent: MyAgent, debug: true)

Or toggle at runtime:

    :ok = AgentServer.set_debug(pid, true)

Retrieve recent events (newest-first):

    {:ok, events} = AgentServer.recent_events(pid, limit: 10)

Each event has the shape `%{at: monotonic_ms, type: atom(), data: map()}`.
Event types include `:signal_received` and `:directive_started`.

Returns `{:error, :debug_not_enabled}` if debug mode is off.

> **Note:** This is a development aid, not an audit log. Events are not
> persisted and the buffer has fixed capacity.

## Orphans and Adoption

If a child is configured with `on_parent_death: :continue` or `:emit_orphan`,
the runtime clears the current parent reference immediately when the logical
parent dies:

- `state.parent` becomes `nil`
- `agent.state.__parent__` becomes `nil`
- the former parent is preserved in `state.orphaned_from`
- the former parent is preserved in `agent.state.__orphaned_from__`

This prevents children from continuing to route signals to a dead parent via
`Jido.Agent.Directive.emit_to_parent/3`.

Reattachment is explicit. A replacement parent can adopt the live child with
`Jido.Agent.Directive.adopt_child/3`, which refreshes the child's live parent
reference and monitoring relationship.

Relationship bindings are mirrored into `Jido.RuntimeStore`, so when a child
later restarts it rehydrates its current logical parent from instance runtime
state instead of falling back to stale startup metadata.

## Timeout Diagnostics

When `await_completion/2` times out, it returns a diagnostic map:

    {:error, {:timeout, %{
      hint: "Agent is idle but await_completion is blocking",
      server_status: :idle,
      queue_length: 0,
      iteration: nil,
      waited_ms: 5000
    }}}

Use this to understand why the agent hasn't completed:
- `:idle` with empty queue → agent finished but state doesn't match await condition
- `:waiting` → strategy is waiting (e.g., for LLM response)
- `:running` → still processing directives

# `server`

```elixir
@type server() :: pid() | atom() | {:via, module(), term()} | String.t()
```

# `adopt_child`

```elixir
@spec adopt_child(server(), pid() | String.t(), term(), map()) ::
  {:ok, pid()} | {:error, term()}
```

Adopts a live child into this agent's logical child map.

This updates both the child's live parent reference and the manager's tracked
`state.children` map before returning.

# `alive?`

```elixir
@spec alive?(server()) :: boolean()
```

Check if the agent server process is alive.

# `attach`

```elixir
@spec attach(server(), pid()) :: :ok | {:error, term()}
```

Attaches a process to this agent, tracking it as an active consumer.

When attached, the agent will not idle-timeout. The agent monitors the
attached process and automatically detaches it on exit.

Used by `Jido.Agent.InstanceManager` to track LiveView sockets, WebSocket handlers,
or any process that needs the agent to stay alive.

## Examples

    {:ok, pid} = Jido.Agent.InstanceManager.get(:sessions, key)
    :ok = Jido.AgentServer.attach(pid)

    # With explicit owner
    :ok = Jido.AgentServer.attach(pid, socket_pid)

# `await_completion`

```elixir
@spec await_completion(
  server(),
  keyword()
) :: {:ok, map()} | {:error, term()}
```

Wait for an agent to reach a terminal status (`:completed` or `:failed`).

This is an event-driven wait - the caller blocks until the agent's state
transitions to a terminal status, then receives the result immediately.
No polling is involved.

## Options

- `:status_path` - Path to status field in agent.state (default: `[:status]`)
- `:result_path` - Path to result field (default: `[:last_answer]`)
- `:error_path` - Path to error field (default: `[:error]`)

## Returns

- `{:ok, %{status: :completed | :failed, result: any()}}` - Agent reached terminal status
- `{:error, :not_found}` - Server not found
- Exits with `{:timeout, ...}` if GenServer.call times out

## Examples

    {:ok, result} = AgentServer.await_completion(pid, timeout: 10_000)

# `call`

```elixir
@spec call(server(), Jido.Signal.t(), timeout()) :: {:ok, struct()} | {:error, term()}
```

Synchronously sends a signal and waits for processing.

Returns the updated agent struct after signal processing.
Directives are still executed asynchronously via the drain loop.

## Returns

* `{:ok, agent}` - Signal processed successfully
* `{:error, :not_found}` - Server not found via registry
* `{:error, :invalid_server}` - Unsupported server reference
* Exits with `{:noproc, ...}` if process dies during call

## Examples

    {:ok, agent} = Jido.AgentServer.call(pid, signal)
    {:ok, agent} = Jido.AgentServer.call("agent-id", signal, 10_000)

# `cast`

```elixir
@spec cast(server(), Jido.Signal.t()) :: :ok | {:error, term()}
```

Asynchronously sends a signal for processing.

Returns immediately. The signal is processed in the background.

## Returns

* `:ok` - Signal queued successfully
* `{:error, :not_found}` - Server not found via registry
* `{:error, :invalid_server}` - Unsupported server reference

## Examples

    :ok = Jido.AgentServer.cast(pid, signal)
    :ok = Jido.AgentServer.cast("agent-id", signal)

# `child_spec`

```elixir
@spec child_spec(keyword() | map()) :: Supervisor.child_spec()
```

Returns a child_spec for supervision.

# `detach`

```elixir
@spec detach(server(), pid()) :: :ok | {:error, term()}
```

Detaches a process from this agent.

If this was the last attachment and `idle_timeout` is configured,
the idle timer starts.

Note: You don't need to call this explicitly if the attached process
exits normally — the monitor will handle cleanup automatically.

## Examples

    :ok = Jido.AgentServer.detach(pid)

# `recent_events`

```elixir
@spec recent_events(
  server(),
  keyword()
) :: {:ok, [map()]} | {:error, term()}
```

Retrieves recent debug events from the agent's event buffer.

Events are returned newest-first. Each event includes:
- `:at` - Monotonic timestamp in milliseconds
- `:type` - Event type atom (e.g., `:signal_received`, `:directive_started`)
- `:data` - Event-specific data map

Returns `{:error, :debug_not_enabled}` if debug mode is off.

## Options

- `:limit` - Maximum number of events to return (default: all, max 50)

## Examples

    {:ok, events} = AgentServer.recent_events(pid, limit: 10)

# `set_debug`

```elixir
@spec set_debug(server(), boolean()) :: :ok | {:error, term()}
```

Enables or disables debug mode at runtime.

When debug mode is enabled, the agent records recent events in a ring buffer
for diagnostic purposes.

## Examples

    :ok = AgentServer.set_debug(pid, true)
    # ... run some operations ...
    {:ok, events} = AgentServer.recent_events(pid)

# `start`

```elixir
@spec start(keyword() | map()) :: DynamicSupervisor.on_start_child()
```

Starts an AgentServer under `Jido.AgentSupervisor`.

## Examples

    {:ok, pid} = Jido.AgentServer.start(agent: MyAgent)
    {:ok, pid} = Jido.AgentServer.start(agent: MyAgent, id: "my-agent")

# `start_link`

```elixir
@spec start_link(keyword() | map()) :: GenServer.on_start()
```

Starts an AgentServer linked to the calling process.

## Options

See module documentation for full list of options.

## Examples

    {:ok, pid} = Jido.AgentServer.start_link(agent: MyAgent)
    {:ok, pid} = Jido.AgentServer.start_link(agent: MyAgent, id: "custom-123")
    {:ok, pid} = Jido.AgentServer.start_link(jido: MyApp.Jido, agent: MyAgent)

# `state`

```elixir
@spec state(server()) :: {:ok, Jido.AgentServer.State.t()} | {:error, term()}
```

Gets the full State struct for an agent.

## Returns

* `{:ok, state}` - Full State struct retrieved
* `{:error, :not_found}` - Server not found via registry
* `{:error, :invalid_server}` - Unsupported server reference

## Examples

    {:ok, state} = Jido.AgentServer.state(pid)
    {:ok, state} = Jido.AgentServer.state("agent-id")

# `status`

```elixir
@spec status(server()) :: {:ok, Jido.AgentServer.Status.t()} | {:error, term()}
```

Gets runtime status for an agent process.

Returns a `Status` struct combining the strategy snapshot with process metadata.
This provides a stable API for querying agent status without depending on internal
`__strategy__` state structure.

## Returns

* `{:ok, status}` - Status struct with snapshot and metadata
* `{:error, :not_found}` - Server not found via registry
* `{:error, :invalid_server}` - Unsupported server reference

## Examples

    {:ok, agent_status} = Jido.AgentServer.status(pid)

    # Check completion
    if agent_status.snapshot.done? do
      IO.puts("Result: " <> inspect(agent_status.snapshot.result))
    end

    # Use delegate helpers
    case Status.status(agent_status) do
      :success -> {:done, Status.result(agent_status)}
      :failure -> {:error, Status.details(agent_status)}
      _ -> :continue
    end

# `stop_child`

```elixir
@spec stop_child(server(), term(), term()) :: :ok | {:error, term()}
```

Requests graceful termination of a tracked child agent.

This is the runtime counterpart to `Directive.stop_child/2`.

# `stream_status`

```elixir
@spec stream_status(
  server(),
  keyword()
) :: Enumerable.t()
```

Streams status updates by polling at regular intervals.

Returns a Stream that yields status snapshots. Useful for monitoring agent
execution without manual polling loops.

## Options

- `:interval_ms` - Polling interval in milliseconds (default: 100)

## Examples

    # Poll until completion
    AgentServer.stream_status(pid, interval_ms: 50)
    |> Enum.reduce_while(nil, fn status, _acc ->
      case Status.status(status) do
        :success -> {:halt, {:ok, Status.result(status)}}
        :failure -> {:halt, {:error, Status.details(status)}}
        _ -> {:cont, nil}
      end
    end)

    # Take first 10 snapshots
    AgentServer.stream_status(pid)
    |> Enum.take(10)

# `touch`

```elixir
@spec touch(server()) :: :ok | {:error, term()}
```

Touches the agent to reset the idle timer.

Use this for request-based activity tracking (e.g., HTTP requests)
where you don't want to maintain a persistent attachment.

## Examples

    # In a controller
    {:ok, pid} = Jido.Agent.InstanceManager.get(:sessions, key)
    :ok = Jido.AgentServer.touch(pid)

# `via_tuple`

```elixir
@spec via_tuple(String.t(), module()) :: {:via, Registry, {module(), term()}}
```

Returns a via tuple for Registry-based naming.

## Examples

    name = Jido.AgentServer.via_tuple("agent-id", MyApp.Jido.Registry)
    GenServer.call(name, :get_state)

# `via_tuple`

```elixir
@spec via_tuple(String.t(), module(), keyword()) ::
  {:via, Registry, {module(), term()}}
```

# `whereis`

```elixir
@spec whereis(module(), String.t()) :: pid() | nil
```

Looks up an agent by ID in a specific registry.

Returns the pid if found, nil otherwise.

## Examples

    pid = Jido.AgentServer.whereis(MyApp.Jido.Registry, "agent-123")
    # => #PID<0.123.0>

# `whereis`

```elixir
@spec whereis(module(), String.t(), keyword()) :: pid() | nil
```

