# `CMDC.Checkpoint.Snapshot`
[🔗](https://github.com/tupleyun/cmdc/blob/v0.5.0/lib/cmdc/checkpoint/snapshot.ex#L1)

会话快照标准 payload。

统一 `CMDC.Checkpoint.save/3` 的输出与 `load/2` 的输入。
含 `@schema_version` 便于未来 schema 演进时向前兼容。

## 不可序列化字段策略

Agent.State 中以下字段在 `save/2` 时会被**自动剥离**，
并在 `resume_session!/2` 时由 plugin `:session_start` hook 重建：

- `pid()` 字段（如 `:agent_pid` / `:owner_pid`）
- `Stream` / `Task` 引用
- `:reference` 字段（如 `:monitor_ref`）

这一策略避免序列化死引用、同时保证 plugin 状态可以靠
`:session_start` 重新初始化。Snapshot 会保留这些字段名加 `:_stripped` 标记
以便恢复时知道哪些 plugin 需要重置。

## Examples

    iex> snap = Snapshot.new(session_id: "sess-1", state: %{messages: []})
    iex> snap.schema_version
    1

# `serializable`

```elixir
@type serializable() :: map() | struct()
```

可被序列化为 snapshot 的负载（通常是 `Agent.State` 或 map）。

# `t`

```elixir
@type t() :: %CMDC.Checkpoint.Snapshot{
  checkpoint_id: String.t(),
  label: String.t() | nil,
  metadata: map(),
  saved_at: DateTime.t(),
  schema_version: pos_integer(),
  session_id: String.t(),
  state: serializable(),
  stripped_fields: [atom()]
}
```

# `new`

```elixir
@spec new(keyword()) :: t()
```

构建新的 Snapshot，自动生成 `checkpoint_id` + `saved_at`，
并剥离不可序列化字段。

## 选项

- `:session_id` — 必填
- `:state` — 必填（Agent.State 或任意 map）
- `:label` — 可选标签
- `:metadata` — 用户附加 map（默认 `%{}`）

# `redact`

```elixir
@spec redact(t(), (serializable() -&gt; serializable())) :: t()
```

对 snapshot 的 `:state` 字段应用一个 redact 函数，返回新的 Snapshot。

常见用途：backend 在 `save/3` 前去除敏感字段（API key / token / PII），
让 CMDC 不强制 encryption_at_rest，但给集成方留官方 hook 接 Cloak / KMS。

## 行为

- `redact_fn` 接受当前 `snapshot.state`（map 或 struct）返回处理后的 map / struct
- 其他字段（checkpoint_id / session_id / saved_at / stripped_fields / metadata / label）保持不变
- 不变更 `schema_version`

## 示例

    # 去掉所有以 :api_ / :token 开头的 key
    Snapshot.redact(snap, fn state ->
      Enum.reject(state, fn {k, _v} ->
        ks = to_string(k)
        String.starts_with?(ks, "api_") or String.contains?(ks, "token")
      end)
      |> Map.new()
    end)

    # 接 Cloak 加密敏感字段
    Snapshot.redact(snap, &MyApp.Vault.encrypt_sensitive/1)

# `schema_version`

```elixir
@spec schema_version() :: pos_integer()
```

返回 schema 版本号常量。

---

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