Jido.Agent.Directive (Jido v2.2.0)

View Source

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
  • %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.

Summary

Types

Built-in core directives.

Restart policy for spawned AgentServer children.

t()

Any external directive struct (core or extension).

Functions

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

Creates a Cron directive for recurring scheduled execution.

Creates a CronCancel directive to stop a recurring job.

Creates an Emit directive.

Creates an Emit directive targeting the agent's parent.

Creates an Emit directive targeting a specific process by PID.

Creates an Error directive.

Creates a RunInstruction directive.

Creates a Schedule directive.

Creates a Spawn directive.

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

Creates a Stop directive.

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

Types

core()

Built-in core directives.

restart_policy()

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

Restart policy for spawned AgentServer children.

t()

@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.

Functions

adopt_child(child, tag, opts \\ [])

@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(cron_expr, message, opts \\ [])

@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(job_id)

@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(signal, dispatch \\ nil)

@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(agent, signal, extra_opts \\ [])

@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(signal, pid, extra_opts \\ [])

@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(error, context \\ nil)

@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(instruction, opts \\ [])

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(delay_ms, message)

Creates a Schedule directive.

Examples

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

spawn(child_spec, tag \\ nil)

@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(agent, tag, options \\ [])

@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)

stop(reason \\ :normal)

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

Creates a Stop directive.

Examples

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

stop_child(tag, reason \\ :normal)

@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)