Use the Jidoka module from application code. It builds, runs, inspects, and resumes agents.

Build

Use the DSL for normal agent modules:

defmodule MyApp.Assistant do
  use Jidoka.Agent

  agent :assistant do
    model "openai:gpt-4o-mini"
    instructions "Answer clearly and briefly."
  end
end

Use agent/1 when an agent comes from runtime data:

{:ok, spec} =
  Jidoka.agent(
    id: "assistant",
    model: "openai:gpt-4o-mini",
    instructions: "Answer clearly and briefly."
  )

Use import/2 for JSON or YAML strings:

{:ok, spec} =
  Jidoka.import("""
  agent:
    id: assistant
    model: openai:gpt-4o-mini
    instructions: Answer clearly and briefly.
  """)

Use export/2 when you need portable agent data:

{:ok, yaml} = Jidoka.export(MyApp.Assistant, format: :yaml)

Use plan/1 when you want the executable turn data before running:

{:ok, plan} = Jidoka.plan(MyApp.Assistant)

Reserve agent!/1 and plan!/1 for boot paths and tests where invalid data should raise.

Run

Use chat/3 for product code that only needs text:

{:ok, text} = Jidoka.chat(MyApp.Assistant, "Draft a short status update.")

Use turn/3 when you need the full result:

{:ok, result} =
  Jidoka.turn(MyApp.Assistant, "Draft a short status update.")

result.content
result.events
result.journal.results

Both functions accept a DSL module, Agent.Spec, Turn.Plan, process id, pid, or session where appropriate.

Agent modules also generate bound convenience functions, such as MyApp.Assistant.chat/2 and MyApp.Assistant.run_turn/2. The facade is the canonical documentation path. Use a generated function when the shorter, bound-agent form makes local code clearer.

Result Shapes

The result shape changes when the caller owns a session. Keep the updated session from each call.

Call targetSuccess or pause shape
Agent, spec, plan, or hosted agent with chat/3{:ok, text}
Caller-managed session with chat/3{:ok, updated_session, text}
Agent, spec, plan, or hosted agent with turn/3{:ok, %Jidoka.Turn.Result{}}
Paused direct turn{:hibernate, snapshot}
Paused caller-managed session{:hibernate, updated_session, snapshot}
Any failed call{:error, reason}

await/2 returns the same normalized result shape as the async request that it waits for.

Stream For UI

Use chat_async/3 when a UI needs to start work now and collect events while the turn runs:

{:ok, request} =
  Jidoka.chat_async(MyApp.Assistant, "Write a two paragraph summary.",
    stream: true
  )

stream = Jidoka.stream(request)

for event <- stream do
  if delta = Jidoka.Stream.text_delta(event) do
    IO.write(delta)
  end
end

{:ok, text} = Jidoka.await(request)

stream/2 yields Jidoka.Event values. await/2 returns the same normalized shape as chat/3.

Use cancel/2 to stop an active request. The result is typed and terminal.

{:ok, cancellation} = Jidoka.cancel(request)
{:cancelled, ^cancellation} = Jidoka.await(request)

Keep State

Use sessions for multi-turn conversations:

{:ok, session} = Jidoka.session(MyApp.Assistant, "conversation-123")

{:ok, session, text} =
  Jidoka.chat(session, "Remember that my account is A1001.")

{:ok, session, text} =
  Jidoka.chat(session, "Which account did I mention?")

Use session/3 when passing a store or runtime options:

{:ok, session} =
  Jidoka.session(MyApp.Assistant, "conversation-123",
    store: {Jidoka.Session.Store.InMemory, pid: store_pid}
  )

Resume

When a control pauses a turn, turn/3 returns a snapshot:

case Jidoka.turn(MyApp.RefundAgent, "Refund order A1001") do
  {:ok, result} ->
    result.content

  {:hibernate, snapshot} ->
    {:ok, [review]} = Jidoka.pending_reviews(snapshot)
    Jidoka.approve(snapshot, review)
end

resume/2 accepts a snapshot struct, snapshot map, or serialized snapshot string.

Use pending_reviews/1, approve/3, and deny/3 for common approval UI flows:

{:ok, [review]} = Jidoka.pending_reviews(snapshot)

{:ok, result} = Jidoka.approve(snapshot, review)

{:error, reason} =
  Jidoka.deny(snapshot, review, reason: :operator_rejected)

Use resume/2 directly when the application has already built a Jidoka.Review.Response.

Host In A Process

Use process hosting when the application wants a long-lived, addressable agent:

{:ok, _pid} = Jidoka.start_agent(MyApp.Assistant, id: "assistant-1")

{:ok, text} = Jidoka.chat("assistant-1", "Are you running?")

pid = Jidoka.whereis("assistant-1")

:ok = Jidoka.stop_agent("assistant-1")

Hosted agents run as Jido.AgentServer processes. Turns still use the same runtime.

Inspect

Use preflight/3 for prompt and tool wiring:

{:ok, preflight} = Jidoka.preflight(MyApp.Assistant, "What can you do?")
preflight.prompt.messages

Use inspect/2 for a readable compiled view:

Jidoka.inspect(MyApp.Assistant)

Use project/1 for data maps in tests, traces, and UI projections:

projection = Jidoka.project(MyApp.Assistant.spec())
projection.id

Handle Errors

Use the error helpers at application boundaries:

case Jidoka.chat(MyApp.Assistant, "Hello") do
  {:ok, text} ->
    text

  {:error, reason} ->
    Logger.warning(Jidoka.format_error(reason))
    {:error, Jidoka.error_to_map(reason)}
end

error_to_map/1 sanitizes likely credential fields.

Handoffs

Handoffs are routing state. They tell your application which agent should own future turns for a conversation:

Jidoka.handoff("conversation-123")
#=> nil or %{agent_id: "...", ...}

:ok = Jidoka.reset_handoff("conversation-123")

Handoff operations happen inside turns. These helpers only read or clear the owner state.

Function Picker

NeedUse
Build spec from dataagent/1
Import JSON/YAMLimport/2
Export JSON/YAMLexport/2
Compile runtime planplan/1
Final textchat/3
Full turn resultturn/3
Async UI requestchat_async/3, cancel/2
Event streamstream/2
Await async resultawait/2
Multi-turn statesession/2 or session/3
Continue a paused turnresume/2
List pending reviewspending_reviews/1
Approve a reviewapprove/3
Deny a reviewdeny/3
Start hosted processstart_agent/2
Stop hosted processstop_agent/2
Lookup hosted processwhereis/2
Prompt/tool preflightpreflight/3
Human-readable inspectioninspect/2
Data projectionproject/1
Format error stringformat_error/1
Error map for logs/UIerror_to_map/1
Current handoff ownerhandoff/1
Clear handoff ownerreset_handoff/1

Testing

Use real providers in product guides and demos. In tests, inject fake capabilities:

llm = fn _intent, _journal, _ctx ->
  {:ok, %{type: :final, content: "ok"}}
end

assert {:ok, "ok"} = Jidoka.chat(MyApp.Assistant, "ping", llm: llm)

See Testing And Evals for operation capabilities, golden tests, and integration tests.

Next