# `Raxol.AgentClientProtocol.Schema.SessionUpdate`
[🔗](https://github.com/DROOdotFOO/raxol-agent/blob/raxol_agent_client_protocol-v0.1.0/packages/raxol_agent_client_protocol/lib/raxol/agent_client_protocol/schema/session_update.ex#L1)

The `session/update` notification's `sessionUpdate`-discriminated union:
real-time feedback the agent streams back to the client during a prompt
turn (message/thought chunks, tool call lifecycle, the execution plan,
available-command changes, mode changes, and the unstable config-option/
session-info updates).

Every variant's payload is a plain wire object carrying its own fields
alongside a `"sessionUpdate"` discriminator key set to the variant's tag
(snake_case, matching the tag atom exactly) -- there is no separate
nesting level for the payload, mirroring how `McpServer` tags its `http`/
`sse` variants with a sibling `"type"` key. Discriminator dispatch on
decode uses explicit, whitelisted `"sessionUpdate"` string clauses (never
`String.to_atom/1`); an unrecognized or missing discriminator is a
`{:error, _}`, not a raise or a silent default.

`from_json/1` is total: it never raises, always returning `{:ok, t}` or
`{:error, reason}`. `to_json/1` delegates to each variant payload
module's own `to_json/1` and adds the `"sessionUpdate"` tag; per package
convention, tagged-tuple unions like this one (see also `ContentBlock`,
`McpServer`, `ToolCallContent`) do not get a `Jason.Encoder` impl of
their own -- only genuine structs do.

## Variants ported (all ten f1729 defines, plus the oracle's eleventh)

  * `:user_message_chunk` / `:agent_message_chunk` / `:agent_thought_chunk`
    -- `ContentChunk` payload
  * `:tool_call` -- `Raxol.AgentClientProtocol.Schema.ToolCall`
  * `:tool_call_update` -- `Raxol.AgentClientProtocol.Schema.ToolCallUpdate`
  * `:plan` -- `Raxol.AgentClientProtocol.Schema.Plan`
  * `:available_commands_update` -- `AvailableCommandsUpdate`
  * `:current_mode_update` -- `CurrentModeUpdate`
  * `:config_option_update` (unstable) --
    `Raxol.AgentClientProtocol.Schema.Unstable.ConfigOptionUpdate`
  * `:session_info_update` (unstable) --
    `Raxol.AgentClientProtocol.Schema.Unstable.SessionInfoUpdate`
  * `:usage_update` -- `Raxol.AgentClientProtocol.Schema.UsageUpdate`, the
    pinned v1.19.0 oracle's eleventh variant (context-window/cost update),
    absent from f1729; ported straight from `priv/schema-oracle/v1/schema.json`
    `$defs/UsageUpdate` (+ its nested `$defs/Cost`), no upstream attribution.

## Closed gap: `ContentChunk.messageId`

The pinned oracle documents an additional optional `messageId` wire field
on `ContentChunk` (groups chunks belonging to the same streamed message)
that f1729's `ACP.ContentChunk` lacked; it is now ported below as
`ContentChunk.message_id` (optional, absent-safe on both decode and
encode).

The `{"sessionId", "update", "_meta"}` envelope that wraps a
`SessionUpdate` for the wire is
`Raxol.AgentClientProtocol.Schema.LifecycleExtras.SessionNotification`.
There is no `AgentNotification` routing union: `"session/update"` is
routed by its `Raxol.AgentClientProtocol.MethodTable` row.

Ported from the MIT `f1729/agent_client_protocol` (c) 2025 f1729; see NOTICE.md.

# `t`

```elixir
@type t() ::
  {:user_message_chunk, Raxol.AgentClientProtocol.Schema.ContentChunk.t()}
  | {:agent_message_chunk, Raxol.AgentClientProtocol.Schema.ContentChunk.t()}
  | {:agent_thought_chunk, Raxol.AgentClientProtocol.Schema.ContentChunk.t()}
  | {:tool_call, Raxol.AgentClientProtocol.Schema.ToolCall.t()}
  | {:tool_call_update, Raxol.AgentClientProtocol.Schema.ToolCallUpdate.t()}
  | {:plan, Raxol.AgentClientProtocol.Schema.Plan.t()}
  | {:available_commands_update,
     Raxol.AgentClientProtocol.Schema.AvailableCommandsUpdate.t()}
  | {:current_mode_update,
     Raxol.AgentClientProtocol.Schema.CurrentModeUpdate.t()}
  | {:config_option_update,
     Raxol.AgentClientProtocol.Schema.Unstable.ConfigOptionUpdate.t()}
  | {:session_info_update,
     Raxol.AgentClientProtocol.Schema.Unstable.SessionInfoUpdate.t()}
  | {:usage_update, Raxol.AgentClientProtocol.Schema.UsageUpdate.t()}
```

# `from_json`

```elixir
@spec from_json(term()) :: {:ok, t()} | {:error, term()}
```

Total: never raises. Dispatches on the `"sessionUpdate"` discriminator via
explicit whitelisted string clauses (no `String.to_atom/1`); an
unrecognized discriminator value, a map with none at all, or a non-map
argument all return a descriptive `{:error, _}`. `"sessionUpdate"` is
stripped before delegating to the variant payload's own `from_json/1` so
it never leaks into that payload's `_meta`.

# `to_json`

```elixir
@spec to_json(t()) :: map()
```

---

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