# `Raxol.AgentClientProtocol.Router`
[🔗](https://github.com/DROOdotFOO/raxol-agent/blob/raxol_agent_client_protocol-v0.1.0/packages/raxol_agent_client_protocol/lib/raxol/agent_client_protocol/router.ex#L222)

Table-driven JSON-RPC method routing for ACP: `decode/4` classifies and
type-decodes an inbound wire method + params into a tagged tuple,
`dispatch/4` calls the matching handler callback, and `result_marker/1`
tells the sending side how to decode a pending outbound request's
response.

Every function clause here is generated at compile time from
`Raxol.AgentClientProtocol.MethodTable.rows/0` by the internal
`Router.Codegen` module (not part of the public docs; `@moduledoc
false`) -- editing the table is the only way to change routing behavior;
there is no hand-maintained parallel list of method strings anywhere in
this module.

## What this module does NOT do (by design)

* **No capability gating.** `Connection` consults `MethodTable`'s
  `capability` field and must reject a gated-and-unnegotiated method with
  -32601 BEFORE ever calling `decode/4` -- `Router` has no
  capability awareness at all.
* **No `$/cancel_request` or `session/cancel` clauses.** Both are
  `Connection`-routed session-control paths pre-filtered on the wire
  method string before `decode/4` is called. Calling `decode(side, :notification,
  "$/cancel_request", _)` or `decode(side, :notification,
  "session/cancel", _)` falls through to the catch-all like any other
  unrecognized method -- callers must pre-filter, not rely on `Router` to
  special-case them.
* **No pending-map bookkeeping.** `result_marker/1` is a pure lookup;
  storing `pending[id] = %{marker: ..., from: ...}` and reading it back on
  response arrival (markers computed once at send time; ids include `nil`)
  is `Connection`'s job.

## Decoding

    iex> Raxol.AgentClientProtocol.Router.decode(:agent, :request, "session/prompt", %{"sessionId" => "s1", "prompt" => []})
    {:ok, {:prompt, %Raxol.AgentClientProtocol.Schema.AgentTypes.PromptRequest{session_id: "s1", prompt: []}}}

    iex> Raxol.AgentClientProtocol.Router.decode(:agent, :request, "_vendor/thing", %{"x" => 1})
    {:ok, {:ext_request, "_vendor/thing", %{"x" => 1}}}

    iex> Raxol.AgentClientProtocol.Router.decode(:agent, :request, "no/such/method", %{})
    {:error, :method_not_found}

# `decoded`

```elixir
@type decoded() ::
  {atom(), struct() | nil}
  | {:ext_request, String.t(), map()}
  | {:ext_notification, String.t(), map()}
```

A decoded, dispatchable inbound message: `{callback_tag, decoded_params_or_nil}`.

# `result_marker`

```elixir
@type result_marker() :: {:decode, module()} | :ext | :unknown
```

What `Connection` should do with a pending outbound request's response.

# `side`

```elixir
@type side() :: :agent | :client
```

Which side is decoding/dispatching: the side that HANDLES the row (see `MethodTable.rows_for_side/1`).

# `decode`

```elixir
@spec decode(side(), Raxol.AgentClientProtocol.MethodTable.kind(), String.t(), term()) ::
  {:ok, decoded()} | {:error, term()}
```

Classify and decode an inbound JSON-RPC method + params for `side`.

Returns `{:ok, decoded}` for a recognized core/registered row or an
`"_"`-prefixed ext passthrough, `{:error, :method_not_found}` for an
unrecognized method (⇒ -32601 at the caller), or `{:error, reason}` when a
recognized row's `params` fail to decode (⇒ -32602 at the caller).

Never creates an atom from `wire` -- unknown and ext methods stay
binaries end-to-end.

# `dispatch`

```elixir
@spec dispatch(side(), module(), decoded(), term()) :: term()
```

Dispatch a decoded message (as produced by `decode/4`) to `handler`'s
matching callback. Request callbacks are called with `(params, ctx)` (or
`(ctx)` alone for `params: nil` rows); notification callbacks
are called with `(params, ctx)` and this function always returns `:ok`
regardless of what the callback itself returns.

# `result_marker`

```elixir
@spec result_marker(String.t()) :: result_marker()
```

Look up how the sending side should decode the response to an outbound
request for `wire`, computed once at send time -- never re-derive
this from the wire string when the response actually arrives; store the
returned marker in the pending-request entry instead.

---

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