Skip to main content

guides/public-facade.md

# Public Facade

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

## Build

Use the DSL for normal agent modules:

```elixir
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:

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

Use `import/2` for JSON or YAML strings:

```elixir
{: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:

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

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

```elixir
{: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:

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

Use `turn/3` when you need the full result:

```elixir
{: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.

## Stream For UI

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

```elixir
{: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`.

## Keep State

Use sessions for multi-turn conversations:

```elixir
{: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:

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

## Resume

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

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

  {:hibernate, snapshot} ->
    review = snapshot.metadata["pending_review"]
    approval = Jidoka.Review.Response.approve(review)
    Jidoka.resume(snapshot, approval: approval)
end
```

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

## Host In A Process

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

```elixir
{: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:

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

Use `inspect/2` for a readable compiled view:

```elixir
Jidoka.inspect(MyApp.Assistant)
```

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

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

## Handle Errors

Use the error helpers at application boundaries:

```elixir
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:

```elixir
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

| Need | Use |
| --- | --- |
| Build spec from data | `agent/1` |
| Import JSON/YAML | `import/2` |
| Export JSON/YAML | `export/2` |
| Compile runtime plan | `plan/1` |
| Final text | `chat/3` |
| Full turn result | `turn/3` |
| Async UI request | `chat_async/3` |
| Event stream | `stream/2` |
| Await async result | `await/2` |
| Multi-turn state | `session/2` or `session/3` |
| Continue a paused turn | `resume/2` |
| Start hosted process | `start_agent/2` |
| Stop hosted process | `stop_agent/2` |
| Lookup hosted process | `whereis/2` |
| Prompt/tool preflight | `preflight/3` |
| Human-readable inspection | `inspect/2` |
| Data projection | `project/1` |
| Format error string | `format_error/1` |
| Error map for logs/UI | `error_to_map/1` |
| Current handoff owner | `handoff/1` |
| Clear handoff owner | `reset_handoff/1` |

## Testing

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

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

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

See [Testing And Evals](testing-and-evals.md) for operation capabilities,
golden tests, and integration tests.

## Next

- [Getting Started](getting-started.md) - build and run the first agent.
- [Core Concepts](core-concepts.md) - the data model behind the facade.
- [Configuration](configuration.md) - default model, generation, loop, and
  timeout settings.
- [Sessions And Stores](sessions-and-stores.md) - durable conversations.
- [Streaming](streaming.md) - event streams for UI.