# `Minga.RenderModel.UI.AgentChat`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/render_model/ui/agent_chat.ex#L1)

Semantic agent chat model.

Describes the agent conversation view: visibility, runtime status, the active
model name and thinking level, the prompt buffer plus its cell-grid metadata
(cursor, vim mode, line counts), an optional prompt completion popup, an
optional pending approval banner, an optional help overlay, and the list of
conversation messages with their stable BEAM-assigned IDs.

This is pure data with domain fields and **core types only**. It does not
reference any product module; the editor builder pre-resolves every agent
struct into the core views defined here before putting them on the model. The
GUI adapter
(`Minga.Frontend.Adapter.GUI.AgentChatEncoder`) owns the wire encoding. A
hidden panel is just `%AgentChat{visible?: false}`; the encoder derives a
change-detection fingerprint with `:erlang.phash2/1`, so no sentinel is needed.

## Messages

`messages` is a list of `{id, body}` tuples where `id` is a stable uint32 the
GUI uses as a persistent identity. `body` is one of the resolved core message
forms produced by the editor builder:

  * `{:user, text}` / `{:user, text, attachments}`
  * `{:assistant, text}`
  * `{:styled_assistant, styled_lines}`
  * `{:thinking, text, collapsed?}`
  * `{:tool_call, %AgentChat.ToolCallView{}}`
  * `{:styled_tool_call, %AgentChat.ToolCallView{}, styled_lines}`
  * `{:approval_tool_call, %AgentChat.ApprovalView{}}`
  * `{:system, text, level}`
  * `{:usage, %AgentChat.Usage{}}`

Bare body tuples (without an `id` wrapper) are also accepted and encode with
ID `0`, matching the historical wire behaviour.

# `message`

```elixir
@type message() :: {pos_integer(), message_body()} | message_body()
```

A message with its stable GUI identity, or a bare body that encodes with ID 0.

# `message_body`

```elixir
@type message_body() ::
  {:user, String.t()}
  | {:user, String.t(), term()}
  | {:assistant, String.t()}
  | {:styled_assistant, [styled_line()]}
  | {:thinking, String.t(), boolean()}
  | {:tool_call, Minga.RenderModel.UI.AgentChat.ToolCallView.t()}
  | {:styled_tool_call, Minga.RenderModel.UI.AgentChat.ToolCallView.t(),
     [styled_line()]}
  | {:approval_tool_call, Minga.RenderModel.UI.AgentChat.ApprovalView.t()}
  | {:system, String.t(), atom()}
  | {:usage, Minga.RenderModel.UI.AgentChat.Usage.t()}
```

A conversation message body.

# `styled_line`

```elixir
@type styled_line() :: [styled_run()]
```

A line of styled runs.

# `styled_run`

```elixir
@type styled_run() ::
  {String.t(), non_neg_integer(), non_neg_integer(), non_neg_integer()}
  | {String.t(), non_neg_integer(), non_neg_integer(), non_neg_integer(),
     String.t()}
```

A styled text run: {text, fg_rgb, bg_rgb, flags} or with a trailing url.

# `t`

```elixir
@type t() :: %Minga.RenderModel.UI.AgentChat{
  help_groups: [{String.t(), [{String.t(), String.t()}]}],
  help_visible?: boolean(),
  messages: [message()],
  model_name: String.t(),
  pending_approval: Minga.RenderModel.UI.AgentChat.PendingApproval.t() | nil,
  prompt: String.t(),
  prompt_completion: Minga.RenderModel.UI.AgentChat.PromptCompletion.t() | nil,
  prompt_cursor_col: non_neg_integer(),
  prompt_cursor_line: non_neg_integer(),
  prompt_line_count: non_neg_integer(),
  prompt_vim_mode: atom() | nil,
  prompt_visible_rows: non_neg_integer(),
  status: atom(),
  thinking_level: String.t(),
  visible?: boolean()
}
```

---

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