Jido

Hex.pm Hex Docs CI License

Jido is a declarative actor and agent framework for Elixir. You declare what an Agent is, instantiate that declaration as an Agent value, and run the value as an OTP actor when you need a live process.

Version 3.0.0-beta.2 is an evaluation candidate with breaking changes from V2 and beta.1. See the migration guide for required changes and the release record for checks and known limits. External storage service tests and distributed ownership guarantees require separate evidence.

Core model

Jido follows the declarative style common in Elixir. The declaration describes the Agent. It does not run an imperative process loop.

  1. Declare an Agent definition with its data schema, routes, Plugins, and metadata.
  2. Instantiate the definition with an identity and initial state.
  3. Route Signals to one Action or Flow.
  4. Let the executable propose the next domain state and Directives.
  5. Validate and commit the complete Agent value.
  6. Let the Agent Server dispatch runtime effects after commit.

Direct Jido.Agent.cmd/3 returns a candidate Agent and Directives; the Server commits live state. Actions and Flows can perform synchronous I/O before they return. A failed Turn preserves committed Agent state but does not undo external work that already completed. Applications own external idempotency and recovery. State assembly is repeatable for fixed inputs and executable results.

The Agent callback and Plugin code cannot replace private Agent Server state.

One %Jido.Agent{} has two valid forms. A definition has id: nil and state: nil. An instance has a non-empty id and validated state. A value that has only an id or only state is invalid.

Agent modules provide declarative agent and routes blocks, with an optional as: route setting for a Signal constructor. Direct map and keyword construction, module construction, and the JSON-compatible Codec use the same Agent validation. See the Jido.Agent and Jido.Agent.Codec API documentation.

Example

defmodule MyApp.Counter do
  use Jido.Agent, name: "counter"

  agent do
    schema(Zoi.object(%{count: Zoi.integer() |> Zoi.default(0)}))
  end

  routes do
    signal_source("/example")

    route "counter.increment", as: :increment do
      action %{amount: amount},
        name: "increment",
        schema: Zoi.object(%{amount: Zoi.integer()}),
        context: context do
        {:ok, %{context.agent_state | count: context.agent_state.count + amount}}
      end

      defaults(%{amount: 1})
    end
  end
end

agent = MyApp.Counter.instantiate!(id: "counter-1")

signal =
  Jido.Signal.new!(
    "counter.increment",
    %{amount: 2},
    source: "/example"
  )

{:ok, candidate, []} = MyApp.Counter.cmd(agent, signal)
candidate.state.count
# => 2

cmd/2 is the main entry point for an Agent value. It routes one Signal and returns a candidate Agent plus the Directives that a runtime can dispatch. The original Agent value stays unchanged.

Run the same Agent value as a live actor when it needs process identity, serialized message handling, persistence, or runtime effects:

{:ok, _jido} = Jido.start()
{:ok, counter} = Jido.start_agent(agent)

{:ok, committed_agent} = Jido.AgentServer.call(counter, signal)
committed_agent.state.count
# => 2

The default instance is also implicit for Agent lookup, listing, counts, stop, hibernate, and thaw. Pass an instance as the first argument only when the application runs more than one Jido supervisor.

The route setting as: :increment creates increment_signal/1,2. Each route can name one helper. Omit as: to generate no helper. Use the Signal with cmd/2 or Jido.AgentServer.call/3:

{:ok, increment_signal} = MyApp.Counter.increment_signal(%{amount: 3})
{:ok, candidate, []} = MyApp.Counter.cmd(agent, increment_signal)

{:ok, committed_agent} =
  with {:ok, signal} <- MyApp.Counter.increment_signal(%{amount: 3}) do
    Jido.AgentServer.call(counter, signal, [])
  end

committed_agent.state.count
# => 5

Agent Plugins

Define a Plugin with use Jido.Plugin and the callbacks it needs. One module can prepare input, reduce an owned state field, manage a live runtime, convert its owned value for persistence, and contribute static Topology entries. Jido selects these capabilities from the documented callbacks. A Plugin cannot change an incoming Signal. Pure and live data enter execution through the separate prepared and runtime slots at context.plugin_inputs[Package]. Results enter through the normal Agent Signal mailbox. See the Jido.Plugin API docs.

Persistence

Persistence is optional. Configure a stable namespace and one binary adapter on the Jido instance:

defmodule MyApp.Jido do
  use Jido,
    otp_app: :my_app,
    namespace: "my-app/agents",
    persistence: {Jido.Persistence.Ecto, repo: MyApp.Repo}
end

Agents inherit this adapter unless their start options override or disable it. A new persistent activation writes revision zero before its start call succeeds. A successful Agent commit is stored before the Server reports success. Normal durable delete writes a tombstone. hibernate/2 saves and stops one Server. thaw/3 restores and starts it. Jido does not start or supervise the application storage process. See the persistence adapter guide for the Ecto dependency and migration.

Installation

After beta.2 is published, use the beta package from Hex:

def deps do
  [
    {:jido, "~> 3.0.0-beta.2"}
  ]
end

For local development, point your application at this checkout:

def deps do
  [
    {:jido, path: "../jido"}
  ]
end

Guides and validation

Start with the Getting Started Livebook, then build your first Agent. Read Actors, Agents, and Jido for the framework model and Agent Definitions and Instances for the value contract.

The guide set also covers Agent Turns, Plugin contracts, actor lifecycle, topologies, persistence and recovery, and operations. Use the example systems catalog to find an executable contract test. If you have V2 application code, use the migration guide.

Design documents contain deferred proposals. They do not define the API implemented by this branch. Cluster-exclusive ownership is not supported.

License

Copyright 2024-2026 Jido contributors.

Licensed under the Apache License, Version 2.0. See LICENSE.