# `PhoenixKitAgent.Schemas.Message`
[🔗](https://github.com/BeamLabEU/phoenix_kit_agent/blob/v0.1.0/lib/phoenix_kit_agent/schemas/message.ex#L1)

One message of a chat (`phoenix_kit_agent_messages`): what the user wrote, a
model answer (with the tool calls it asked for), a tool result, or a notice
(a limit, an error, a refusal). The table is also the assistant's journal of
tool calls: who, what, when, and the outcome.

`conversation_uuid`, `ai_request_uuid`, `assistant_message_uuid` and
`confirmation_uuid` are set by the caller, never cast. `tool_name` is the
registry name of the tool, not the name shown to the model; `tool_result` is
the truncated result, without confirmation tokens or other control fields.

A write the user is asked to confirm (V2 of the migrations) is a `tool`
message with `tool_outcome: "needs_confirmation"`, the uuid of core's
confirmation in `confirmation_uuid` and the `assistant` message whose tool
call it answers in `assistant_message_uuid`; how it ended is a `notice` with
the same `confirmation_uuid` and its outcome (`confirmed`, `rejected`,
`expired`, `changed`, `failed`). One without such a notice is still waiting.

`changeset/2` is the full set of fields, for the turn loop
(`PhoenixKitAgent.Turns`) only; `user_changeset/2` is what a person writes —
the text of a `user` message and nothing else. Both cut NUL characters from
every text they keep (PostgreSQL refuses them in `text` and `jsonb`).

`tool_summary` is virtual: the read functions of
`PhoenixKitAgent.Conversations` fill it for a `tool` message in place of
the result (see there). So is `card`: the chat panel puts there what it
shows of a confirmation (`PhoenixKitAgent.Confirmations.cards/4`).

# `t`

```elixir
@type t() :: %PhoenixKitAgent.Schemas.Message{
  __meta__: term(),
  ai_request_uuid: term(),
  assistant_message_uuid: term(),
  card: term(),
  confirmation_uuid: term(),
  content: term(),
  conversation: term(),
  conversation_uuid: term(),
  cost_micro_usd: term(),
  error: term(),
  input_tokens: term(),
  inserted_at: term(),
  latency_ms: term(),
  model: term(),
  output_tokens: term(),
  role: term(),
  tool_call_id: term(),
  tool_calls: term(),
  tool_name: term(),
  tool_outcome: term(),
  tool_result: term(),
  tool_summary: term(),
  turn_ref: term(),
  uuid: term()
}
```

# `changeset`

```elixir
@spec changeset(t(), map()) :: Ecto.Changeset.t()
```

Builds a message from all its content fields — for the turn loop only, never
from what a client sent.

# `roles`

```elixir
@spec roles() :: [String.t()]
```

The message roles: `user`, `assistant`, `tool`, `notice`.

# `tool_outcomes`

```elixir
@spec tool_outcomes() :: [String.t()]
```

The outcomes of a tool call.

# `user_changeset`

```elixir
@spec user_changeset(t(), map()) :: Ecto.Changeset.t()
```

A message the user writes: role `user` and the non-blank `content`; every
other attribute is ignored.

---

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