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

The function RPC of protocol v1.0, as a value.

Two directions meet here.

**The agent calls a renderer function** (`callRendererFunction`).
`call_renderer_function/3` resolves the function in the catalog the
call names, checks that an agent may call it, resolves its arguments
against the data of the surface the call names (or against an empty
data model), validates them, runs it and returns the
`rendererFunctionResponse` message to send back. It always returns
one: every failure is an error response, never an exception.

**The renderer calls an agent function** (`callAgentFunction`).
`call_agent_function/4` registers a pending call and returns the
message to send. The call is settled later, by one of:

  * `handle_agent_function_response/2` — the agent answered;
  * `timeout/2` — the host's timer for the call fired;
  * `cancel/3` and `dispose/2` — the host gave up.

Each of these returns the settlements it caused:
`{:resolved, call_id, value}` or `{:rejected, call_id, error}`.

Nothing here runs a process or reads a clock. A call's timeout is data
(`pending_timeout/2`): the host starts a timer for it and reports it
with `timeout/2`.

## Error codes

Responses and rejections use `INVALID_FUNCTION_CALL` (an unknown
surface, catalog or function, a caller or activation violation, bad
arguments, a malformed call), `EXECUTION_ERROR` (the function failed,
or an argument's expression did), `TIMEOUT`, `CANCELLED`, `DISPOSED`,
`DUPLICATE` and `UNKNOWN_ERROR` (an agent error without a code).

# `pending`
*since 0.1.0* 

```elixir
@type pending() :: %{name: String.t(), timeout_ms: number() | nil}
```

# `settlement`
*since 0.1.0* 

```elixir
@type settlement() ::
  {:resolved, String.t(), term()} | {:rejected, String.t(), A2UICore.Error.t()}
```

# `t`
*since 0.1.0* 

```elixir
@type t() :: %A2UICore.RPC{
  catalogs: [A2UICore.Catalog.t()],
  default_timeout_ms: number(),
  disposed?: boolean(),
  next_id: pos_integer(),
  pending: %{optional(String.t()) =&gt; pending()}
}
```

# `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 and returns the
`callAgentFunction` message to send.

`call` is a function call object (`"@call"` or `"call"`, `"catalogId"`,
`"args"`). Options: `:function_call_id` (a counter-based id otherwise),
`:timeout_ms` (the handler's default otherwise) and `:version`.

Fails with a `:rpc` error — `DISPOSED`, `INVALID_FUNCTION_CALL` for a
call without a name, `DUPLICATE` for an id that is already pending.

# `call_renderer_function`
*since 0.1.0* 

```elixir
@spec call_renderer_function(
  t(),
  A2UICore.Message.Operation.CallRendererFunction.t(),
  keyword()
) ::
  map()
```

Executes an agent's `callRendererFunction` and returns the
`rendererFunctionResponse` message.

The function runs in the catalog the call names (`catalogId`). Its
arguments are resolved against `:context`, the data context of the
surface the call names (`surfaceId`); a call that names no surface
runs in a context of its own, with an empty data model and the call's
catalog as its default catalog. An argument whose expression fails
makes the call fail with `EXECUTION_ERROR`; the resolved arguments are
then validated against the function's schema.

Options:

  * `:context` — the `A2UICore.Resolution.DataContext` of the surface
    the call names (`nil` when there is no such surface, which fails the
    call);
  * `:user_activated` — whether the call happens inside a user gesture,
    which functions that require activation need (default `false`).

# `cancel`
*since 0.1.0* 

```elixir
@spec cancel(t(), String.t(), String.t()) :: {t(), [settlement()]}
```

Rejects a pending call because the host abandoned it, with `CANCELLED`.
Ignored when the call is no longer pending.

# `dispose`
*since 0.1.0* 

```elixir
@spec dispose(t(), String.t()) :: {t(), [settlement()]}
```

Rejects every pending call with `CANCELLED` and stops the handler:
later renderer-function calls answer `DISPOSED` and agent calls fail.
Idempotent.

# `disposed?`
*since 0.1.0* 

```elixir
@spec disposed?(t()) :: boolean()
```

Whether the handler has been disposed.

# `handle_agent_function_response`
*since 0.1.0* 

```elixir
@spec handle_agent_function_response(
  t(),
  A2UICore.Message.Operation.AgentFunctionResponse.t()
) ::
  {t(), [settlement()]}
```

Settles the pending call an agent's `agentFunctionResponse` answers.
A response to a call that is not pending is ignored.

# `new`
*since 0.1.0* 

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

Creates the handler over `catalogs`, the catalogs functions are
resolved in.

Options: `:default_timeout_ms` (default 30 000) for calls that name no
timeout of their own; zero or less means no timeout.

Raises `ArgumentError` when `catalogs` is empty.

# `pending`
*since 0.1.0* 

```elixir
@spec pending(t()) :: [String.t()]
```

The ids of the calls still waiting for the agent, sorted.

# `pending_timeout`
*since 0.1.0* 

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

The timeout of a pending call in milliseconds, or `nil` when the call
is not pending or has none. The host's timer is the one that fires it.

# `timeout`
*since 0.1.0* 

```elixir
@spec timeout(t(), String.t()) :: {t(), [settlement()]}
```

Rejects a pending call whose timer fired, with `TIMEOUT`. Ignored when
the call is no longer pending.

---

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