3.5 KiB
3.5 KiB
切换对话诊断发现
当前已知
- 会话文件仍包含用户消息,暂未发现数据层删除。
- 切换通过 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,也会静默停用心跳,不再 将协议探测错误显示到聊天区。新版服务端已声明该能力,安全重启后可恢复探测。