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

Typed directive structs for `Jido.Agent`.

A *directive* is a pure description of an external effect for the runtime
(e.g. `Jido.AgentServer`) to execute. Agents and strategies **never**
interpret or execute directives; they only emit them.

## Signal Integration

The Emit directive integrates with `Jido.Signal` and `Jido.Signal.Dispatch`:

- `%Emit{}` - Dispatch a signal via configured adapters (pid, pubsub, bus, http, etc.)

## Design

Directives are bare structs - no tuple wrappers. This enables:
- Clean pattern matching on struct type
- Protocol-based dispatch for extensibility
- External packages can define custom directives

## Core Directives

  * `%Emit{}` - Dispatch a signal via `Jido.Signal.Dispatch`
  * `%Error{}` - Signal an error (wraps `Jido.Error.t()`)
  * `%Spawn{}` - Spawn a generic BEAM child process (fire-and-forget, no tracking)
  * `%SpawnAgent{}` - Spawn a child Jido agent with full hierarchy tracking
  * `%AdoptChild{}` - Attach an orphaned or unattached child to the current parent
  * `%StopChild{}` - Request a tracked child agent to stop gracefully
  * `%StartSensor{}` - Start or replace a tagged sensor runtime
  * `%StopSensor{}` - Stop a tagged sensor runtime
  * `%Schedule{}` - Schedule a delayed message
  * `%RunInstruction{}` - Execute an instruction at runtime and route result to `cmd/2`
  * `%Stop{}` - Stop the agent process (self)

## Usage

    alias Jido.Agent.Directive

    # Emit a signal (runtime will dispatch via configured adapters)
    %Directive.Emit{signal: my_signal}
    %Directive.Emit{signal: my_signal, dispatch: {:pubsub, topic: "events"}}
    %Directive.Emit{signal: my_signal, dispatch: {:pid, target: pid}}

    # Schedule for later
    %Directive.Schedule{delay_ms: 5000, message: :timeout}

    # Execute instruction at runtime
    %Directive.RunInstruction{instruction: instruction, result_action: :fsm_instruction_result}

## Extensibility

External packages can define their own directive structs:

    defmodule MyApp.Directive.CallLLM do
      defstruct [:model, :prompt, :tag]
    end

The runtime dispatches on struct type, so no changes to core are needed.

# `core`

```elixir
@type core() ::
  Jido.Agent.Directive.Emit.t()
  | Jido.Agent.Directive.Error.t()
  | Jido.Agent.Directive.Spawn.t()
  | Jido.Agent.Directive.SpawnAgent.t()
  | Jido.Agent.Directive.AdoptChild.t()
  | Jido.Agent.Directive.StopChild.t()
  | Jido.Agent.Directive.StartSensor.t()
  | Jido.Agent.Directive.StopSensor.t()
  | Jido.Agent.Directive.Schedule.t()
  | Jido.Agent.Directive.RunInstruction.t()
  | Jido.Agent.Directive.Stop.t()
  | Jido.Agent.Directive.Cron.t()
  | Jido.Agent.Directive.CronCancel.t()
```

Built-in core directives.

# `restart_policy`

```elixir
@type restart_policy() :: :permanent | :temporary | :transient
```

Restart policy for spawned AgentServer children.

# `t`

```elixir
@type t() :: struct()
```

Any external directive struct (core or extension).

This is intentionally `struct()` so external packages can define
their own directive structs without modifying this type.

# `adopt_child`

```elixir
@spec adopt_child(pid() | String.t(), term(), keyword()) ::
  Jido.Agent.Directive.AdoptChild.t()
```

Creates an AdoptChild directive for explicitly attaching a child to the current parent.

## Options

- `:meta` - Metadata to write into the adopted child's new parent reference (map)

## Examples

    Directive.adopt_child(child_pid, :worker_1)
    Directive.adopt_child("child-agent-id", :worker_1, meta: %{restored: true})

# `cron`

```elixir
@spec cron(term(), term(), keyword()) :: Jido.Agent.Directive.Cron.t()
```

Creates a Cron directive for recurring scheduled execution.

## Options

- `:job_id` - Logical id for the job (for upsert/cancel)
- `:timezone` - Timezone identifier

## Examples

    Directive.cron("* * * * *", tick_signal)
    Directive.cron("@daily", cleanup_signal, job_id: :daily_cleanup)
    Directive.cron("0 9 * * MON", weekly_signal, job_id: :monday_9am, timezone: "America/New_York")

# `cron_cancel`

```elixir
@spec cron_cancel(term()) :: Jido.Agent.Directive.CronCancel.t()
```

Creates a CronCancel directive to stop a recurring job.

## Examples

    Directive.cron_cancel(:heartbeat)
    Directive.cron_cancel(:daily_cleanup)

# `emit`

```elixir
@spec emit(term(), term()) :: Jido.Agent.Directive.Emit.t()
```

Creates an Emit directive.

If `dispatch` is omitted, runtime will use `AgentServer` `default_dispatch`.
When no default is configured, runtime falls back to emitting to the current
agent process (`self()`).

## Examples

    Directive.emit(signal)
    Directive.emit(signal, {:pubsub, topic: "events"})

# `emit_to_parent`

```elixir
@spec emit_to_parent(struct(), term(), Keyword.t()) ::
  Jido.Agent.Directive.Emit.t() | nil
```

Creates an Emit directive targeting the agent's parent.

The agent's state must have a `__parent__` field containing a `ParentRef` struct.
This field is automatically populated when an agent is spawned via the
`SpawnAgent` directive or explicitly reattached via `AdoptChild`.

Returns `nil` if the agent has no current parent. Orphaned agents clear
`__parent__` during the orphan transition, so `emit_to_parent/3` becomes
unavailable until the child is explicitly adopted again. Former parent
provenance remains available via `agent.state.__orphaned_from__`, but that
field is intentionally not used for routing. Use `List.wrap/1` to safely
handle the result when building directive lists.

## Options

Same as `emit_to_pid/3`.

## Examples

    # In a child agent's action:
    defmodule WorkDoneAction do
      use Jido.Action, name: "work.done", schema: []

      def run(_params, context) do
        reply = Signal.new!("worker.result", %{answer: 42}, source: "/worker")
        directive = Directive.emit_to_parent(context.agent, reply)
        {:ok, %{}, List.wrap(directive)}
      end
    end

    # With sync delivery
    Directive.emit_to_parent(agent, signal, delivery_mode: :sync)

# `emit_to_pid`

```elixir
@spec emit_to_pid(term(), pid(), Keyword.t()) :: Jido.Agent.Directive.Emit.t()
```

Creates an Emit directive targeting a specific process by PID.

This is a convenience for sending signals directly to another agent or process.

## Options

All options are passed to the `:pid` dispatch adapter:
- `:delivery_mode` - `:async` (default) or `:sync`
- `:timeout` - Timeout for sync delivery (default: 5000)

## Examples

    Directive.emit_to_pid(signal, some_pid)
    Directive.emit_to_pid(signal, worker_pid, delivery_mode: :sync)

# `error`

```elixir
@spec error(term(), atom() | nil) :: Jido.Agent.Directive.Error.t()
```

Creates an Error directive.

## Examples

    Directive.error(Jido.Error.validation_error("Invalid input"))
    Directive.error(error, :normalize)

# `run_instruction`

```elixir
@spec run_instruction(
  Jido.Instruction.t(),
  keyword()
) :: Jido.Agent.Directive.RunInstruction.t()
```

Creates a RunInstruction directive.

## Options

- `:result_action` - Internal action atom/module to receive execution results (default: `:instruction_result`)
- `:meta` - Optional metadata echoed in result payload (map)

## Examples

    Directive.run_instruction(instruction)
    Directive.run_instruction(instruction, result_action: :fsm_instruction_result)

# `schedule`

```elixir
@spec schedule(non_neg_integer(), term()) :: Jido.Agent.Directive.Schedule.t()
```

Creates a Schedule directive.

## Examples

    Directive.schedule(5000, :timeout)
    Directive.schedule(1000, {:check, ref})

# `spawn`

```elixir
@spec spawn(term(), term()) :: Jido.Agent.Directive.Spawn.t()
```

Creates a Spawn directive.

## Examples

    Directive.spawn({MyWorker, arg: value})
    Directive.spawn(child_spec, :worker_1)

# `spawn_agent`

```elixir
@spec spawn_agent(term(), term(), keyword()) :: Jido.Agent.Directive.SpawnAgent.t()
```

Creates a SpawnAgent directive for spawning child agents with hierarchy tracking.

## Options

- `:opts` - Additional options for the child AgentServer (map)
  - Supports child startup options like `:id`, `:initial_state`, and `:on_parent_death`
  - Does not support InstanceManager lifecycle/persistence options like `:storage`,
    `:idle_timeout`, `:lifecycle_mod`, `:pool`, `:pool_key`, or `:restored_from_storage`
- `:meta` - Metadata to pass to the child via parent reference (map)
- `:restart` - Child restart policy under supervision (default: `:transient`)

## Examples

    Directive.spawn_agent(MyWorkerAgent, :worker_1)
    Directive.spawn_agent(MyWorkerAgent, :processor, opts: %{initial_state: %{batch_size: 100}})
    Directive.spawn_agent(MyWorkerAgent, :handler, meta: %{assigned_topic: "events"})
    Directive.spawn_agent(MyWorkerAgent, :durable, restart: :permanent)

# `start_sensor`

```elixir
@spec start_sensor(term(), module(), keyword()) ::
  Jido.Agent.Directive.StartSensor.t()
```

Creates a StartSensor directive to start or replace a tagged sensor runtime.

## Options

- `:config` - Sensor configuration map (default: `%{}`)
- `:meta` - Metadata stored with the tracked sensor (default: `%{}`)
- `:replace?` - Whether to replace an existing sensor for the tag (default: `true`)
- `:link?` - Link the sensor to the owning AgentServer so abnormal sensor
  exits can take the owner down (default: `false`)

## Examples

    Directive.start_sensor(:market_data, MyApp.MarketDataSensor)
    Directive.start_sensor({:poller, "AAPL"}, MyApp.MarketDataSensor,
      config: %{symbol: "AAPL", interval: 1000}
    )

# `stop`

```elixir
@spec stop(term()) :: Jido.Agent.Directive.Stop.t()
```

Creates a Stop directive.

## Examples

    Directive.stop()
    Directive.stop(:shutdown)

# `stop_child`

```elixir
@spec stop_child(term(), term()) :: Jido.Agent.Directive.StopChild.t()
```

Creates a StopChild directive to gracefully stop a tracked child agent.

## Examples

    Directive.stop_child(:worker_1)
    Directive.stop_child(:processor, :shutdown)

# `stop_sensor`

```elixir
@spec stop_sensor(term(), term()) :: Jido.Agent.Directive.StopSensor.t()
```

Creates a StopSensor directive to stop a tagged sensor runtime.

## Examples

    Directive.stop_sensor(:market_data)
    Directive.stop_sensor({:poller, "AAPL"}, :reconfigured)

