Files
cc-web/.planning/2026-09-13-session-switch-diagnosis/findings.md
2026-09-13 23:59:27 +08:00

56 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 切换对话诊断发现
## 当前已知
- 会话文件仍包含用户消息,暂未发现数据层删除。
- 切换通过 WebSocket `load_session` 请求完成,刷新会创建新连接。
- 服务端日志存在大量心跳终止和断线重连记录。
- 前端同时处理会话缓存、加载请求代次、历史分页、运行态恢复和滚动位置。
## 可复核证据
- `server.js:5656-5658` 普通加载只发送最近 `INITIAL_HISTORY_COUNT = 12` 条;
`server.js:10890-10907` 同时发送完整 `historyTotal`、`historyCursor`、
`historyBaseIndex` 和 `historyTruncated`,普通大历史请求可以出现
`messages=12, historyTotal=99, historyCursor=87, historyPending=false`。
- 原逻辑 `public/app.js:2607-2611` 用 `!payload.historyPending` 推导
`complete`,因此上述部分快照会被标成完整;随后
`session_info → cacheSessionSnapshot → getSessionCacheDisposition →
showCachedSession` 可在 A → B → A 时绕过服务端重新加载。
- 修复后 `normalizeSessionSnapshot` 和 `isCompleteSessionSnapshot` 共同要求
游标归零、未截断、未等待分片且缓冲数覆盖总数;缓存写入和读取都复用同一
谓词。历史分片按 `historyBaseIndex` 合并,最后一页才转换为完整缓存。
- 部分快照现在进入独立 `sessionHistoryBuffers`,仅供后续 `load_history_page`
合并;`getSessionCacheDisposition` 永远不会把它当作可展示缓存。分页游标归
零后才调用 `cacheSessionSnapshot`,因此 A → B → A 不会复用半截历史。
- `logs/process.log` 中存在 `ws_heartbeat_terminate`,例如
`2026-09-13T07:30:03.445Z`,`missedPongs=4`、`lastActivityAgeMs=150471`;
`server.js:2216-2221` 的 `markWsActivity` 已会把 `isAlive` 恢复为 `true`,
因此该记录更符合浏览器/反向代理长期未返回 pong 的半断连接,而不是服务端
漏置位。
- 客户端原先只在 `onclose` 触发重连;现增加 `WS_CLIENT_HEARTBEAT_INTERVAL_MS`
(15 秒)与 `WS_CLIENT_HEARTBEAT_TIMEOUT_MS`(10 秒),通过
`client_heartbeat → client_heartbeat_ack` 主动识别浏览器仍显示 `OPEN` 的半断,
再复用现有 `onclose` 流程重放切换请求。
## 问题边界
- 会话文件未发现消息被删除;气泡消失的直接原因是前端错误缓存/重绘,
不是数据库或服务端历史丢失。
- WebSocket 处于浏览器 `OPEN` 但链路半断时,`send(load_session)` 可能无效;
刷新页面会强制新建连接。现在客户端最多约 25 秒主动发现并重连,真实反向
代理是否丢弃控制帧仍需浏览器现场或代理配置验证。
- 未重启现有 `ccweb`,因为当前会话列表中还有另一个 running 对话;源码修复
需在安全窗口重启后才会进入线上进程。
## 2026-09-13 新增线上证据
- 用户截图中的 `Unknown type: client_heartbeat` 证实线上 PM2 仍运行未包含
`client_heartbeat` 分支的旧 `server.js`;前端先部署心跳会被旧服务端当作业务
错误,并触发连接重连循环。
- `mcp__ccweb__ccweb_list_conversations(status=running)` 显示当前会话之外仍有
一个 running 会话,按项目运维约定本轮不能重启 PM2。
- 已在前端加入 `auth_result.features.clientHeartbeat` 能力闸门:旧服务端不声明
时不发送心跳;即使旧页面已发送并收到 Unknown type,也会静默停用心跳,不再
将协议探测错误显示到聊天区。新版服务端已声明该能力,安全重启后可恢复探测。