# Agent Note: 加载消息标识机制引入前持久化的会话

Status: implemented

[English](2026-07-28-load-pre-identity-session-messages.md) | 中文

## 问题

带标识的不可变消息变更将四种持久化事件载荷替换为完整消息值。现有的 v0 JSONL 和 SQLite 会话仍保留紧邻该变更之前的形状：用户事件和 steering（中途引导）事件直接携带 `content`/`source`，assistant 事件携带 `content`/`provenance`，工具结果则携带 `callId`/`content`/`isError`。这些会话的标头仍与 `SESSION_FORMAT_VERSION` 匹配，但当前形状验证会拒绝它们，导致恢复流程无法构造活跃的 `Session`。

消息表示改变时没有提升版本，导致这些日志无法仅凭标头与当前的 v0 日志区分。运行时需要一条范围受限的导入规则，既能恢复受支持的第一方后端所创建的数据，又不削弱对无关过时事件或格式错误事件的验证。

## 决策

`PersistenceCoordinator` 会在后端解码之后、当前消息验证之前，规范化消息标识机制引入前的四种特定消息载荷。它将载荷现有的语义字段包装进当前按角色区分的消息形状，并为其分配确定性的导入用 `MessageId`：`legacy-message:<session-id>:<event-seq>`。旧版 `tool/result` 的内容替换会继承替换目标导入后的 id，从而保持当前仅改写内容的不变量。

同一项规范化也用于 `load`、`inspect`、无 owner 的已加载状态认领其活跃会话，以及 HMR（热模块替换）前缀接管。因此，前缀比较会将活跃会话的当前形状 seed 与同一份规范化存储视图进行比较。看似当前形状、但字段缺失或无效的包装层不会被修复；不受支持的事件词汇、请求 header、版本和 surface 关系仍沿用现有拒绝路径。

这项升级只发生在读取时。存储中的旧版记录保持不变；会话恢复后，只会在其后追加当前形状的事件。确定性标识使重复加载以及新旧形状混合的日志无需执行后端专用的重写事务，也能复现相同的消息 id。

## 考虑过的替代方案

**按照预发布兼容性立场拒绝这些日志。** 这是处理其他 v0 形状变动的默认方式，但即使每个旧字段都能明确映射到当前消息表示，它仍会导致真实的第一方会话无法恢复。

**就地重写完整的存储日志。** 这会使产物规范化，但违反仅追加存储约定，还需要为 JSONL 和 SQLite 分别实现原子替换机制，并将一次读取兼容性修复扩大为迁移系统。

**每次加载时随机生成 id。** 这些消息会满足类型形状，却无法在检查、恢复、重启以及新旧形状混合追加之间保持稳定标识。

## 后果

消息标识机制引入前的 JSONL 和 SQLite 会话可以恢复，并保留原始的消息内容、来源、assistant 的提供方／模型字段、工具调用关联、错误、元数据和 surface 替换。除此之外，返回事件与当前导入的消息快照无法区分，并且仍然经过深度冻结。

这是一个显式的同版本导入例外，而非通用的 v0 兼容层。若要增加另一个例外，必须在持久化边界提供另一套完整且无歧义的映射；当前数据若格式错误，系统仍会拒绝，而不会猜测如何将其变成有效数据。共享的协调器约定会在内存参考实现、JSONL 和 SQLite 后端上验证这项升级，包括重新加载时的确定性，以及工具结果替换时的标识继承。

## 相关

- [将每条消息创建为带标识的不可变值](../architecture/2026-07-28-identified-immutable-message-values.md)：该记录负责当前的消息标识与不可变性约定。
- [会话持久化作为抽象服务](../architecture/2026-06-14-session-persistence.md)：该记录负责仅追加后端与恢复边界。
