Minga.RenderModel.UI.AgentChat (Minga v0.1.0)

Copy Markdown View Source

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 help overlay, and the resident 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 adapters split the model across two commands: Minga.Frontend.Adapter.GUI.AgentChatEncoder owns gui_agent_chat (0x78) chrome encoding and chrome fingerprinting, Minga.Frontend.Adapter.GUI.AgentTranscriptEncoder owns gui_agent_transcript (0x86) resident framing, and Minga.Frontend.Adapter.GUI.AgentChatMessageCodec owns each transcript message body. A hidden panel is just %AgentChat{visible?: false}; the chrome encoder derives its change-detection fingerprint with :erlang.phash2/1, so no sentinel is needed.

Messages

resident_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}
  • {:assistant_markdown, markdown_blocks}
  • {: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.

Resident transcript

resident_messages rides the dedicated gui_agent_transcript (0x86) stream so the frontend can scroll from local data (#2654). The editor builder applies :agent_transcript_resident_max_bytes as a contiguous most-recent suffix and sets resident_truncated? when it omits older complete messages. The adapter serializes that already-selected model exactly. transcript_epoch is an opaque change token that flips on structural change (session switch, display-start/compaction), driving the resident stream's full-replace-vs-append decision. Both default empty/zero so hidden or non-transcript surfaces produce no resident frame.

Summary

Types

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

A conversation message body.

A line of styled runs.

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

t()

Functions

Selects a contiguous newest resident suffix without shortening any message.

Types

message()

@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()

@type message_body() ::
  {:user, String.t()}
  | {:user, String.t(), term()}
  | {:assistant, String.t()}
  | {:styled_assistant, [styled_line()]}
  | {:assistant_markdown, [Minga.RenderModel.UI.AgentChat.MarkdownBlock.t()]}
  | {: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()

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

A line of styled runs.

styled_run()

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

t()

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

Functions

resident_suffix(messages, max_bytes, entry_size)

@spec resident_suffix([message()], pos_integer(), (message() -> pos_integer())) ::
  {[message()], boolean()}

Selects a contiguous newest resident suffix without shortening any message.