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

A data-binding scope: the absolute path relative bindings resolve
against, the collection-template iteration index, and — when built
for a surface — what is needed to evaluate dynamic values.

The root scope is `"/"`. A component rendered for item `2` of a
`/items` template runs in the nested scope `"/items/2/"`, so its
relative path `name` resolves to `/items/2/name`.

## Path resolution

Follows the reference:

  * an absolute path (starting with `/`) is returned unchanged,
  * `""` and `"."` resolve to the scope itself,
  * any other path is joined to the scope, ignoring trailing slashes
    on the scope.

    iex> ctx = A2UICore.Resolution.DataContext.new("/user")
    iex> A2UICore.Resolution.DataContext.resolve_path(ctx, "name")
    "/user/name"
    iex> A2UICore.Resolution.DataContext.resolve_path(ctx, "/other")
    "/other"
    iex> A2UICore.Resolution.DataContext.resolve_path(ctx, ".")
    "/user"

## Dynamic values

`resolve_dynamic_value/2` evaluates a literal, a data binding
(`{"path": …}` in v0.9, `{"@path": …}` in v1.0) or a function call
(`{"call": …, "args": …}` / `{"@call": …, "args": …}`, optionally with
a `catalogId`) against the data model, recursing through arrays and
objects. It never raises: what the reference reports through the
surface comes back as *effects* next to the value —

  * `{:warning, %{code: "MISSING_DATA_BINDING", path: …, message: …}}`
    when a bound path is absent (once per path per call),
  * `{:error, %{code: "EXPRESSION_ERROR", message: …, expression: …}}`
    when a function call fails; the call's value is then `nil`.

A function can ask for more through its effects — the MCP catalog's
functions do (`A2UICore.Functions.MCP`):

  * `{:write, %{path: path, value: value}}` — write the data model;
  * `{:messages, %{messages: messages, source: :resource | :inline}}` —
    apply A2UI messages.

They come back in evaluation order. A renderer applies them when they
come from an action (`A2UICore.Resolution.NodeResolver.invoke/5`);
rendering ignores them.

## Agent functions

From v1.0 a function that the catalog a call resolves to does not
declare, or declares without an implementation, may be the agent's: a
renderer sends it to the agent as a `callAgentFunction` message. A
scope built with the `:agent_calls` option routes such calls instead
of failing them. The option holds what the renderer knows of the calls
it sent, keyed by `{name, catalog_id, args}` (`catalog_id` is the one
the call names, else `nil`; `args` are resolved):

  * `:pending` — sent, no response yet; the call's value is `nil`;
  * `{:ok, value}` — the agent's response; the call's value is `value`;
  * `{:error, payload}` — the call failed; its value is `nil`.

Every routed call comes back as an
`{:agent_call, %{key: key, call: call}}` effect, where `call` is the
function call object to send (`"@call"`, resolved `"args"`, and the
`"catalogId"` the call named), so that the renderer sends the calls it
has not sent yet. With `agent_calls: {:send, calls}`, the scope of a
function-call action, the call the action makes is always routed
without a lookup, and its effect also carries `action: true`; the
calls in its arguments are looked up in `calls`. A call that names a
catalog the scope does not reach is still a catalog error.

## Who may call

A function the renderer runs is refused, with an `EXPRESSION_ERROR`,
when its catalog reserves it for the agent (`allowedCallers:
"agentOnly"`), and when it requires a user gesture
(`requiresUserActivation: true`, as `openUrl` does) and the scope was
not built for one with `user_activated: true`. A renderer builds its
action scopes that way (`A2UICore.Resolution.NodeResolver.invoke/5`);
property bindings and check conditions never are.

v1.0 objects follow the reserved-key rule
(`A2UICore.Validation.ReservedKeys`): `@@x` is the literal key `@x`,
any other single-`@` key is an `INVALID_RESERVED_KEY` error.

# `agent_call_state`
*since 0.1.0* 

```elixir
@type agent_call_state() :: :pending | {:ok, term()} | {:error, map()}
```

What a renderer knows of an agent function call it sent.

# `agent_calls`
*since 0.1.0* 

```elixir
@type agent_calls() ::
  nil
  | %{optional(term()) =&gt; agent_call_state()}
  | {:send, %{optional(term()) =&gt; agent_call_state()}}
```

The agent function calls a scope knows, by `{name, catalog_id, args}`;
`nil` when the scope does not route calls to the agent.

# `effect`
*since 0.1.0* 

```elixir
@type effect() ::
  {:warning, map()}
  | {:error, map()}
  | {:write, map()}
  | {:messages, map()}
  | {:agent_call, map()}
```

A warning or error produced while resolving, what a function asks for,
or a call routed to the agent.

# `t`
*since 0.1.0* 

```elixir
@type t() :: %A2UICore.Resolution.DataContext{
  agent_calls: agent_calls(),
  catalog: A2UICore.Catalog.t() | nil,
  catalogs: %{optional(String.t()) =&gt; A2UICore.Catalog.t()},
  data_model: A2UICore.State.DataModel.t() | nil,
  index: non_neg_integer() | nil,
  locale: String.t() | nil,
  parent: t() | nil,
  path: String.t(),
  protocol_version: String.t() | nil,
  surface_id: String.t() | nil,
  user_activated: boolean()
}
```

# `for_surface`
*since 0.1.0* 

```elixir
@spec for_surface(A2UICore.State.Surface.t(), keyword()) :: t()
```

Creates the root scope for a `A2UICore.State.Surface`: its data
model, its default catalog and the catalogs it can reach.

Options: `:path` (default `"/"`), `:locale` (default: the surface's),
`:agent_calls` and `:user_activated`.

# `index`
*since 0.1.0* 

```elixir
@spec index(t()) :: non_neg_integer() | nil
```

The active collection index: the nearest scope's recorded index, or
the numeric last segment of its path, walking outward; `nil` outside a
collection.

    iex> root = A2UICore.Resolution.DataContext.new()
    iex> item = A2UICore.Resolution.DataContext.nested(root, "items/3")
    iex> A2UICore.Resolution.DataContext.index(item)
    3
    iex> A2UICore.Resolution.DataContext.index(root)
    nil

# `nested`
*since 0.1.0* 

```elixir
@spec nested(t(), String.t(), non_neg_integer() | nil) :: t()
```

Creates a child scope for `relative_path` (a leading `/` is ignored),
optionally recording the iteration `index` of a collection template.

# `new`
*since 0.1.0* 

```elixir
@spec new(String.t(), keyword()) :: t()
```

Creates a root scope at `path` (default `"/"`). The stored path always
ends with a slash.

Options: `:data_model`, `:catalog` (the default catalog, if any),
`:catalogs` (`%{catalog_id => catalog}` reachable by `catalogId`),
`:protocol_version` (default: the default catalog's), `:surface_id`
(for messages), `:locale`, `:agent_calls` (see
[Agent functions](#module-agent-functions)), `:user_activated` (see
[Who may call](#module-who-may-call); default `false`).

# `resolve_action`
*since 0.1.0* 

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

Resolves an action's dynamic parts: an event's `context` and
`userMessage`, a plain action object, or a function call.

# `resolve_dynamic_value`
*since 0.1.0* 

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

Evaluates a dynamic value; see the module documentation.

# `resolve_path`
*since 0.1.0* 

```elixir
@spec resolve_path(t(), String.t()) :: String.t()
```

Resolves `path` against the scope to an absolute pointer.

# `v1?`
*since 0.1.0* 

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

Whether bindings use the v1.0 `@path`/`@call` spelling, decided by
the scope's protocol version (that of its surface, which outlives a
missing default catalog).

---

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