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

An Agent is an immutable data structure that holds state and can be updated
via commands. This module provides a minimal, data-first API:

- `new/1` - Create a new agent
- `set/2` - Update state directly
- `validate/2` - Validate agent state against schema
- `cmd/2` - Execute actions: `(agent, action) -> {agent, directives}`

## Core Pattern

The fundamental operation is `cmd/2`:

    {agent, directives} = MyAgent.cmd(agent, MyAction)
    {agent, directives} = MyAgent.cmd(agent, {MyAction, %{value: 42}})
    {agent, directives} = MyAgent.cmd(agent, [Action1, Action2])

Key invariants:
- The returned `agent` is **always complete** — no "apply directives" step needed
- `directives` are **runtime-owned external effects only** — they never modify agent state
- Agent decision logic stays explicit and testable

Jido keeps agent decision logic pure. Actions may be pure or effectful.
Directives are for effects you want the runtime to own. If a step needs a
result back now to continue reasoning or update state, an effectful action is
acceptable; if delivery should belong to the runtime or an integration layer,
return a directive.

## Action Formats

`cmd/2` accepts actions in these forms:

- `MyAction` - Action module with no params
- `{MyAction, %{param: value}}` - Action with params
- `%Instruction{}` - Full instruction struct
- `[...]` - List of any of the above (processed in sequence)

## Directives

Directives are effect descriptions for the runtime to interpret. They are
**strictly outbound** - the agent never receives directives as input.

Directives are bare structs (no tuple wrappers). Built-in directives
(see `Jido.Agent.Directive`):

- `%Directive.Emit{}` - Dispatch a signal via `Jido.Signal.Dispatch`
- `%Directive.Error{}` - Signal an error (wraps `Jido.Error.t()`)
- `%Directive.Spawn{}` - Spawn a child process
- `%Directive.Schedule{}` - Schedule a delayed message
- `%Directive.RunInstruction{}` - Execute an instruction at runtime and route result to `cmd/2`
- `%Directive.Stop{}` - Stop the agent process

The Emit directive integrates with `Jido.Signal` for dispatch:

    # Emit with default dispatch config
    %Directive.Emit{signal: my_signal}

    # Emit to PubSub topic
    %Directive.Emit{signal: my_signal, dispatch: {:pubsub, topic: "events"}}

    # Emit to a specific process
    %Directive.Emit{signal: my_signal, dispatch: {:pid, target: pid}}

External packages can define custom directive structs without modifying core.

Directives never modify agent state — that's handled by the returned agent.

## Usage

### Defining an Agent Module

    defmodule MyAgent do
      use Jido.Agent,
        name: "my_agent",
        description: "My custom agent",
        schema: [
          status: [type: :atom, default: :idle],
          counter: [type: :integer, default: 0]
        ]
    end

### Working with Agents

    # Create a new agent (fully initialized including strategy state)
    agent = MyAgent.new()
    agent = MyAgent.new(id: "custom-id", state: %{counter: 10})

    # Execute actions
    {agent, directives} = MyAgent.cmd(agent, MyAction)
    {agent, directives} = MyAgent.cmd(agent, {MyAction, %{value: 42}})
    {agent, directives} = MyAgent.cmd(agent, [Action1, Action2])

    # Update state directly
    {:ok, agent} = MyAgent.set(agent, %{status: :running})

## Strategy Initialization

`new/1` automatically calls `strategy.init/2` to initialize strategy-specific
state. Any directives returned by strategy init are dropped here since they
require a runtime to execute. When using `AgentServer`, it handles strategy
init directives separately during startup.

## Lifecycle Hooks

Agents support two optional callbacks:

- `on_before_cmd/2` - Called before command processing (pure transformations only)
- `on_after_cmd/3` - Called after command processing (pure transformations only)

## State Schema Types

Agent supports two schema formats for state validation:

1. **NimbleOptions schemas** (familiar, legacy):
   ```elixir
   schema: [
     status: [type: :atom, default: :idle],
     counter: [type: :integer, default: 0]
   ]
   ```

2. **Zoi schemas** (recommended for new code):
   ```elixir
   schema: Zoi.object(%{
     status: Zoi.atom() |> Zoi.default(:idle),
     counter: Zoi.integer() |> Zoi.default(0)
   })
   ```

Both are handled transparently by the Agent module.

## Pure Functional Design

The Agent struct is immutable. All operations return new agent structs.
Server/OTP integration is handled separately by `Jido.AgentServer`.

# `action`

```elixir
@type action() :: module() | {module(), map()} | Jido.Instruction.t() | [action()]
```

# `agent_result`

```elixir
@type agent_result() :: {:ok, t()} | {:error, Jido.Error.t()}
```

# `cmd_result`

```elixir
@type cmd_result() :: {t(), [directive()]}
```

# `directive`

```elixir
@type directive() :: Jido.Agent.Directive.t()
```

# `t`

```elixir
@type t() :: %Jido.Agent{
  agent_module: nil | atom(),
  category: nil | binary(),
  description: nil | binary(),
  id: nil | binary(),
  name: nil | binary(),
  schema: any(),
  state: map(),
  tags: [binary()],
  vsn: nil | binary()
}
```

# `checkpoint`
*optional* 

```elixir
@callback checkpoint(agent :: t(), ctx :: map()) :: {:ok, map()} | {:error, term()}
```

Serializes the agent for persistence.

Called by `Jido.Persist.hibernate/2` before writing to storage.
The default implementation passes the full agent state through.
`Jido.Persist` enforces invariants (e.g., stripping `:__thread__`
and storing a pointer) after this callback returns.

## Parameters

- `agent` - The agent to serialize
- `ctx` - Context map (may contain jido instance, options)

## Returns

- `{:ok, serializable_data}` - Data to persist
- `{:error, reason}` - Serialization failed

# `on_after_cmd`
*optional* 

```elixir
@callback on_after_cmd(agent :: t(), action :: term(), directives :: [directive()]) ::
  {:ok, t(), [directive()]}
```

Called after command processing. Can transform the agent or directives.
Must be pure - no side effects. Return `{:ok, agent, directives}` to continue.

Use cases:
- Auto-validate state after changes
- Derive computed fields
- Add invariant checks

# `on_before_cmd`
*optional* 

```elixir
@callback on_before_cmd(agent :: t(), action :: term()) :: {:ok, t(), term()}
```

Called before command processing. Can transform the agent or action.
Must be pure - no side effects. Return `{:ok, agent, action}` to continue.

This hook runs once per `cmd/2` call, with the action as passed (which may be a list).
It is not a per-instruction hook.

Use cases:
- Mirror action params into agent state (e.g., save last_query before processing)
- Add default params that depend on current state
- Enforce invariants or guards before execution

# `restore`
*optional* 

```elixir
@callback restore(data :: map(), ctx :: map()) :: {:ok, t()} | {:error, term()}
```

Restores an agent from persisted data.

Called by `Jido.Persist.thaw/3` after loading from storage.
The Thread is reattached separately by Persist after restore.

If not implemented, a default restoration is used that:
- Creates a new agent with the persisted id
- Merges the persisted state

## Parameters

- `data` - The persisted data (from checkpoint/2)
- `ctx` - Context map (may contain jido instance, options)

## Returns

- `{:ok, agent}` - Restored agent (without thread attached)
- `{:error, reason}` - Restoration failed

# `signal_routes`
*optional* 

```elixir
@callback signal_routes() :: [Jido.Signal.Router.route_spec()]
```

Returns signal routes for this agent.

Routes map signal types to action modules. AgentServer uses these routes
to map incoming signals to actions for execution via cmd/2.

## Route Formats

- `{path, ActionModule}` - Simple mapping (priority 0)
- `{path, ActionModule, priority}` - With priority
- `{path, {ActionModule, %{static: params}}}` - With static params
- `{path, match_fn, ActionModule}` - With pattern matching
- `{path, match_fn, ActionModule, priority}` - Full spec

## Context

The context map currently contains:
- `agent_module` - The agent module

## Examples

    use Jido.Agent,
      name: "my_agent",
      signal_routes: [
        {"user.created", HandleUserCreatedAction},
        {"counter.increment", IncrementAction},
        {"payment.*", fn s -> s.data.amount > 100 end, LargePaymentAction, 10}
      ]

# `signal_routes`
*optional* 

```elixir
@callback signal_routes(ctx :: map()) :: [Jido.Signal.Router.route_spec()]
```

# `new`

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

Creates a new agent from attributes.

For module-based agents, use `MyAgent.new/1` instead.

# `schema`

```elixir
@spec schema() :: Zoi.schema()
```

Returns the Zoi schema for Agent.

# `set`

```elixir
@spec set(t(), map() | keyword()) :: agent_result()
```

Updates agent state by merging new attributes.

# `validate`

```elixir
@spec validate(
  t(),
  keyword()
) :: agent_result()
```

Validates agent state against its schema.

