# `Nous.Session.Event`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/session/event.ex#L1)

One durable fact about a session.

Events are append-only. Nothing is ever mutated or deleted: a compaction
appends a *replace* that shadows a range, and the shadowed events stay in the
log where fork, rewind and audit can still see them.

## Identity lives here, not on `Nous.Message`

`%Nous.Message{}` declares `@primary_key false` — messages genuinely have no
identity, and adding one would change a public struct that
`Nous.run/3` hands back. The event carries `seq`; messages are projected fresh
from the log every time. That is what lets the log be internal while
`result.messages` stays byte-identical.

## Surface and non-surface types

Only four types project into the model-visible surface, and only those may
carry a `:surface_op`:

  * `:system_message`, `:user_message`, `:assistant_message`, `:tool_result`

Everything else is bookkeeping the model never sees — `:tool_call`,
`:turn_start`, `:turn_end`, `:step_start`, `:step_end`, `:request_header`. They
are logged because they are what makes a run reconstructable, but
`Nous.Session.Log.derive_messages/1` projects them to nothing.

> #### Why four and not three {: .info}
>
> The plan specified exactly three surface types, on the assumption that system
> content is assembly-time state rather than history. That assumption does not
> survive contact with the code: `Nous.Plugins.Summarization` appends its
> summary as a **system** message mid-conversation, and
> `build_initial_messages` in `Nous.AgentRunner` puts one at the head. A system
> message is therefore a real, ordered part of the transcript and needs a
> surface type. The *rewrite* of the system prompt in
> `Nous.AgentRunner.PromptAssembly` stays assembly-time and is deliberately not
> an event — see that module.

# `surface_op`

```elixir
@type surface_op() :: :append | {:replace, non_neg_integer(), non_neg_integer()}
```

How this event contributes to the model-visible surface.

`:append` adds it at the end. `{:replace, start_seq, stop_seq}` additionally
shadows every surface event in that inclusive `seq` range — the mechanism
behind non-destructive compaction.

# `t`

```elixir
@type t() :: %Nous.Session.Event{
  data: map(),
  seq: non_neg_integer(),
  time: DateTime.t(),
  type: type()
}
```

# `type`

```elixir
@type type() ::
  :system_message
  | :user_message
  | :assistant_message
  | :tool_result
  | :tool_call
  | :turn_start
  | :turn_end
  | :step_start
  | :step_end
  | :request_header
```

# `new`

```elixir
@spec new(non_neg_integer(), type(), map(), DateTime.t() | nil) ::
  {:ok, t()} | {:error, term()}
```

Build an event, validating type, data shape and `:surface_op`.

Validation happens **here, at append time**, not at persist time. An event that
cannot be serialized is a bug in the caller, and discovering it hours later
when a session is saved makes it someone else's bug.

Returns `{:error, reason}` rather than raising: an event that fails validation
must not take down a live agent run.

# `surface?`

```elixir
@spec surface?(type() | t()) :: boolean()
```

Whether this event contributes to the model-visible surface.

## Examples

    iex> Nous.Session.Event.surface?(:assistant_message)
    true

    iex> Nous.Session.Event.surface?(:step_start)
    false

# `surface_op`

```elixir
@spec surface_op(t()) :: surface_op()
```

The `:surface_op` this event carries. `:append` unless it says otherwise.

# `surface_types`

```elixir
@spec surface_types() :: [type()]
```

The types that project into the model-visible surface.

## Examples

    iex> Nous.Session.Event.surface_types()
    [:system_message, :user_message, :assistant_message, :tool_result]

# `types`

```elixir
@spec types() :: [type()]
```

Every valid event type.

---

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