# `A2UICore.Processor`
[🔗](https://github.com/a2ui-elixir/a2ui/blob/v0.1.1/umbrella/apps/a2ui_core/lib/a2ui_core/processor.ex#L1)

Applies agent→renderer messages to a set of surfaces.

A processor is plain data: `process/2` takes a processor and a raw
payload (a message, a list of messages, a `{"messages": [...]}`
wrapper) and returns the updated processor together with the effects a
host may react to. Nothing is delivered by callback and no process is
involved; the host owns the processor value.

    catalog = A2UICore.Catalog.Basic.catalog("v0.9")
    processor = A2UICore.Processor.new([catalog])

    {:ok, processor, effects} =
      A2UICore.Processor.process(processor, %{
        "version" => "v0.9",
        "createSurface" => %{"surfaceId" => "main", "catalogId" => catalog.catalog_id}
      })

## Semantics

Messages are decoded (`A2UICore.Message.decode/2`) and their operations
applied in order. There is no rollback across operations: when one
fails, the operations before it stay applied and `process/2` returns
`{:error, error, processor}` with that partially updated processor.
Within one `updateComponents` the update is atomic — a failing batch
leaves the surface untouched.

  * `createSurface` — the surface's default catalog is the one named
    by `catalogId`. A v1.0 surface may name none and then has no
    default catalog; before v1.0 the processor's first catalog is the
    fallback. The message's version must be compatible with the catalog
    (`A2UICore.Version`) and a theme is validated against it. The
    surface can use every catalog of its protocol version. Inline
    `dataModel` and `components` are applied right after the surface is
    created.
  * `updateComponents` — each component takes the catalog named by its
    `catalogId`, else the one it already has (when its type is
    unchanged), else the surface's default; from v1.0 a component that
    names none on a surface without a default catalog is an error, and
    so is a function call nested in it whose catalog does not resolve.
    With a validation configuration the batch is validated as a whole
    before it is committed (`A2UICore.Validation.ComponentsUpdate`).
  * `updateDataModel` — writes into the surface's data model.
  * `deleteSurface` — removes the surface; an unknown id is ignored.
  * `callRendererFunction` — runs the function (see `A2UICore.RPC`)
    against the data of the surface its `surfaceId` names, or of no
    surface when it names none, and comes back as an
    `{:rpc_response, message}` effect: the `rendererFunctionResponse` to
    send to the agent. Calls that require user activation need the
    `:user_activated` option.
  * `agentFunctionResponse` — settles the pending agent call it answers
    and comes back as an `{:rpc_settled, call_id, outcome}` effect.
    Neither changes surface state.

Without a validation configuration (`:validation` option) component
batches are committed unchecked.

## Effects

  * `{:surface_created, surface_id}`
  * `{:surface_deleted, surface_id}`
  * `{:components_updated, surface_id, component_ids}`
  * `{:data_model_updated, surface_id, path}`
  * `{:rpc_response, message}`
  * `{:rpc_settled, call_id, {:ok, value} | {:error, error}}`

## Calling the agent

`call_agent_function/4` registers a call and returns the
`callAgentFunction` message. The call is settled by an
`agentFunctionResponse` message, by `timeout_agent_call/2` when the
host's timer for it fires, by `cancel_agent_call/3`, or by `dispose/1`;
the settlements come back as `{:rpc_settled, …}` effects.

# `effect`
*since 0.1.0* 

```elixir
@type effect() ::
  {:surface_created, String.t()}
  | {:surface_deleted, String.t()}
  | {:components_updated, String.t(), [String.t()]}
  | {:data_model_updated, String.t(), String.t()}
  | {:rpc_response, map()}
  | {:rpc_settled, String.t(), {:ok, term()} | {:error, A2UICore.Error.t()}}
```

# `t`
*since 0.1.0* 

```elixir
@type t() :: %A2UICore.Processor{
  catalogs: [A2UICore.Catalog.t()],
  group: A2UICore.State.SurfaceGroup.t(),
  rpc: A2UICore.RPC.t(),
  validation: A2UICore.Validation.Config.t() | nil
}
```

# `agent_call_timeout`
*since 0.1.0* 

```elixir
@spec agent_call_timeout(t(), String.t()) :: number() | nil
```

The timeout, in milliseconds, of a pending agent call: the host starts a
timer for it and reports the expiry with `timeout_agent_call/2`.
`nil` for a call that is not pending or has no timeout.

# `apply_operation`
*since 0.1.0* 

```elixir
@spec apply_operation(t(), A2UICore.Message.Operation.t(), keyword()) ::
  {:ok, t(), [effect()]} | {:error, A2UICore.Error.t(), t()}
```

Applies one operation.

# `apply_operations`
*since 0.1.0* 

```elixir
@spec apply_operations(t(), [A2UICore.Message.Operation.t()], keyword()) ::
  {:ok, t(), [effect()]} | {:error, A2UICore.Error.t(), t()}
```

Applies already-decoded operations in order, as `process/2` does.

# `call_agent_function`
*since 0.1.0* 

```elixir
@spec call_agent_function(t(), String.t(), map(), keyword()) ::
  {:ok, t(), map()} | {:error, A2UICore.Error.t()}
```

Registers a call to an agent function on `surface_id` and returns the
`callAgentFunction` message to send; see `A2UICore.RPC.call_agent_function/4`
for the call and its options.

# `cancel_agent_call`
*since 0.1.0* 

```elixir
@spec cancel_agent_call(t(), String.t(), String.t()) :: {t(), [effect()]}
```

Rejects a pending agent call the host abandoned, with `CANCELLED`.

# `dispose`
*since 0.1.0* 

```elixir
@spec dispose(t()) :: {t(), [effect()]}
```

Rejects every pending agent call with `CANCELLED` and stops answering
function calls. Surfaces are left as they are.

# `new`
*since 0.1.0* 

```elixir
@spec new([A2UICore.Catalog.t()], keyword()) :: t()
```

Creates a processor over `catalogs`. The first catalog is the default
for surfaces that name none.

Options: `:validation` — an `A2UICore.Validation.Config` applied to
component batches (none by default); `:default_timeout_ms` — the
timeout of agent calls that name none (30 000).

Raises `ArgumentError` when `catalogs` is empty.

# `process`
*since 0.1.0* 

```elixir
@spec process(t(), term(), keyword()) ::
  {:ok, t(), [effect()]} | {:error, A2UICore.Error.t(), t()}
```

Decodes `payload` and applies its operations in order.

Returns `{:ok, processor, effects}`, or `{:error, error, processor}`
where `processor` holds whatever was applied before the failure.

Options: `:user_activated` — the messages are processed inside a user
gesture (functions that require activation run only then).

# `renderer_capabilities`
*since 0.1.0* 

```elixir
@spec renderer_capabilities(t(), keyword()) ::
  {:ok, map()} | {:error, A2UICore.Error.t()}
```

The renderer capabilities for the processor's catalogs, keyed by
protocol version.

Options:

  * `:versions` — the protocol versions to describe (required);
  * `:include_inline_catalogs` — add each catalog's definition under
    `"inlineCatalogs"`: its document (`A2UICore.Catalog.to_json/2`)
    from v1.0 on, else a legacy inline catalog whose components are
    `allOf` envelopes over `:component_envelope_ref` (default
    `common_types.json#/$defs/ComponentCommon`), built from the resolved schemas.

Returns `{:error, error}` when no version is given.

# `renderer_data_model`
*since 0.1.0* 

```elixir
@spec renderer_data_model(t(), String.t() | nil) ::
  {:ok, map() | nil} | {:error, A2UICore.Error.t()}
```

The data models of the surfaces that asked for them (`sendDataModel`),
as `%{"version" => …, "surfaces" => %{id => data}}`, or `nil` when no
surface did.

With `version`, only surfaces whose catalog is compatible with it are
included. Without, the version is the one all such surfaces share
(`"v1.0"` when none declares one); surfaces on different versions are
an error.

# `set_data`
*since 0.1.0* 

```elixir
@spec set_data(t(), String.t(), String.t(), term()) ::
  {:ok, t(), [effect()]} | {:error, A2UICore.Error.t()}
```

Writes `value` at `path` of a surface's data model on the renderer's
behalf — user input reaching a two-way binding. It is the same write as
an `updateDataModel` message, but not one the agent sent, so the
validation configuration's `allowed_messages` does not apply.

Returns `{:ok, processor, [{:data_model_updated, surface_id, path}]}`,
or `{:error, error}` for an unknown surface or an invalid path.

# `surface`
*since 0.1.0* 

```elixir
@spec surface(t(), String.t()) :: A2UICore.State.Surface.t() | nil
```

The surface with `id`, or `nil`.

# `surfaces`
*since 0.1.0* 

```elixir
@spec surfaces(t()) :: [A2UICore.State.Surface.t()]
```

All surfaces, sorted by id.

# `timeout_agent_call`
*since 0.1.0* 

```elixir
@spec timeout_agent_call(t(), String.t()) :: {t(), [effect()]}
```

Rejects a pending agent call whose timer fired, with `TIMEOUT`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
