消息持久化
wesgine 对写入 wes_messages 的每条消息执行严格的清洗和校验。
图片剥离(INV-PERSIST-01)
所有写入路径统一调用 stripInlineImages():
| 写入路径 | 处理 |
|---|---|
onRunStart(用户消息) | ✅ 剥离 |
onTurnEnd(助手回复) | ✅ 剥离 |
onToolResult(工具结果) | ✅ 剥离 |
剥离规则
- base64 image block → 移除 Source 或存盘
data:URL file_ref → 移除 Source 或存盘- 非图片附件同样处理
图片保存
当 imageSaveDir 存在时:
- 解码 base64 数据
- 白名单化文件名(
filepath.Base+ MIME 子类型白名单 + 内容 hash 唯一后缀) - 写入磁盘
- 改写为
file_ref磁盘路径
空消息过滤(INV-PERSIST-02)
InsertMessage 返回 (bool, error):
role=assistant且全部 ContentBlock 为空文本(thinking-only 无 text/tool_use)- → 返回
(false, nil)不写入 - 调用方(Tracer)仅在
inserted==true时调用IncrementMessageCount
防止空气泡与 provider 400 拒绝。
FTS5 同步(INV-PERSIST-04)
wes_messages_fts 的触发器必须覆盖三种操作:
| 操作 | Trigger |
|---|---|
| INSERT | ✅ |
| UPDATE | ✅ wes_messages_fts_update |
| DELETE | ✅ |
使用 delete-then-insert 模式同步更新。
Run 摘要消息(INV-PERSIST-03)
persistRunSummaryMessage 的约束:
- 使用
evt.TurnNum(非red.TotalTurns)作为MessageRow.TurnNum - 插入后调用
IncrementMessageCount
文件名安全
白名单化防止路径穿越与同名覆盖:
原始文件名 → filepath.Base → MIME 白名单 → content hash 后缀 → 安全文件名
相关文档
- 数据韧性 →
data-resilience.md - Session 管理 →
session-management.md