Skip to main content

lib/ash_jido.ex

defmodule AshJido do
  @moduledoc """
  Bridge Ash Framework resources with Jido agents.

  Provides two capabilities:

  1. Generates `Jido.Action` modules from Ash actions at compile time
  2. Publishes `Jido.Signal` events from Ash notifier lifecycle events

  ## Usage

      defmodule MyApp.Accounts.User do
        use Ash.Resource,
          domain: MyApp.Accounts,
          extensions: [AshJido]

        actions do
          create :register
          read :by_id
        end

        jido do
          action :register
          action :by_id, name: "get_user"
        end
      end

  Generated modules can be called with `run/2`:

      {:ok, user} = MyApp.Accounts.User.Jido.Register.run(
        %{name: "John"},
        %{domain: MyApp.Accounts, actor: current_user}
      )

  ## Context

  AshJido resolves the Ash domain from `context[:domain]` first, then from the
  resource's static `domain:` configuration. An `ArgumentError` is raised if
  neither is available.

      context = %{
        domain: MyApp.Accounts,       # optional override when the resource has a static domain
        actor: current_user,          # optional: for authorization
        tenant: "org_123",            # optional: for multi-tenancy
        authorize?: true,             # optional: explicit authorization mode
        tracer: [MyApp.Tracer],       # optional: Ash tracer modules
        scope: MyApp.Scope.for(user), # optional: Ash scope
        context: %{request_id: "1"},  # optional: Ash action context
        timeout: 15_000,              # optional: Ash operation timeout
        signal_dispatch: {:pid, target: self()} # optional: override signal dispatch
      }

  ## DSL: Individual Actions

      jido do
        action :create
        action :read, name: "list_users", description: "List all users", load: [:profile]
        action :update, category: "ash.update", tags: ["user-management"], vsn: "1.0.0"
        action :special, output_map?: false
      end

  ## DSL: Bulk Exposure

      jido do
        all_actions
        all_actions except: [:destroy]
        all_actions only: [:create, :read]
        all_actions include_private?: true
        all_actions category: "ash.resource", tags: ["public-api"], vsn: "1.0.0"
        all_actions only: [:read], read_load: [:profile]
      end

  `all_actions` uses Ash's public API boundary by default and expands only
  actions with `public?: true`. Set `include_private?: true` only for trusted
  internal tool catalogs. Generated schemas also omit accepted attributes and
  action arguments marked `public?: false` unless `include_private?: true` is set.

  ## Action Options

  - `name` - Custom Jido action name (default: auto-generated, e.g. `"create_user"`)
  - `module_name` - Custom module name (default: `Resource.Jido.ActionName`)
  - `description` - Action description (default: from Ash action)
  - `category` - Category for discovery/tool organization
  - `tags` - List of tags for categorization (default: `[]`)
  - `vsn` - Optional semantic version identifier for generated action metadata
  - `output_map?` - Convert output structs to maps (default: `true`)
  - `include_private?` - Include private inputs in generated schemas for trusted/internal tools (default: `false`)
  - `load` - Static `Ash.Query.load/2` statement for read actions (default: `nil`)
  - `allowed_loads` - Allowlisted runtime `load` query parameter entries for read actions (default: `nil`)
  - `emit_signals?` - Emit Jido signals from Ash notifications on create/update/destroy (default: `false`)
  - `signal_dispatch` - Default dispatch configuration for emitted signals (default: `nil`)
  - `signal_type` - Override emitted signal type (default: derived)
  - `signal_source` - Override emitted signal source (default: derived)
  - `signal_include` - Data inclusion mode for generated-action signals (default: `:pkey_only`)
  - `telemetry?` - Emit Jido-namespaced telemetry for generated action execution (default: `false`)

  ## Default Naming

  When `name` is not provided:

  - `:create` → `"create_<resource>"` (e.g. `"create_user"`)
  - `:read` with name `:read` → `"list_<resources>"` (e.g. `"list_users"`)
  - `:read` with name `:by_id` → `"get_<resource>_by_id"`
  - `:update` → `"update_<resource>"`
  - `:destroy` → `"delete_<resource>"`
  - custom `:action` → `"<resource>_<action_name>"`

  ## See Also

  - [Getting Started Guide](guides/getting-started.md)
  - [Usage Rules](usage-rules.md)
  - `AshJido.Tools` for listing generated actions and exporting `to_tool/0` payloads
  """

  @sections [AshJido.Resource.Dsl.jido_section()]

  use Spark.Dsl.Extension,
    transformers: [
      AshJido.Resource.Transformers.GenerateJidoActions,
      AshJido.Resource.Transformers.CompilePublications
    ],
    sections: @sections

  @version Mix.Project.config()[:version]

  @doc """
  Returns the version of AshJido.
  """
  def version, do: @version

  @doc false
  def explain(dsl_state, opts) do
    Spark.Dsl.Extension.explain(dsl_state, __MODULE__, nil, opts)
  end
end