101 lines
6.3 KiB
Markdown
101 lines
6.3 KiB
Markdown
# 会话查询与实时订阅契约
|
||
|
||
## 1. 适用范围与触发条件
|
||
|
||
浏览器打开会话、查看更早消息、索引用户目录、定位消息和恢复运行视图时适用。
|
||
已有实现把这些读取绑定到 WebSocket 的 OPEN、鉴权和运行绑定;假 OPEN 时请求可能
|
||
丢在旧连接里,直到45秒超时。历史查询必须有独立 HTTP 通道,重连只恢复实时订阅。
|
||
这项代码事实不能用于断言每次现场故障都来自同一原因。
|
||
|
||
## 2. 接口与函数签名
|
||
|
||
```text
|
||
GET /api/sessions/:id?requestId=<id>
|
||
GET /api/sessions/:id/history?before=<非负整数>&requestId=<id>
|
||
Authorization: Bearer <已有登录令牌>
|
||
|
||
handleSessionQueryApi(req, res, url, encodedId, historyPage = false)
|
||
resolveSessionHistory(session, { count, before, persistedOnly: true })
|
||
loadSession(id, { metadata: true, throwOnError: true })
|
||
|
||
WS → { type: 'subscribe_session', sessionId, requestId, historyRevision }
|
||
WS ← { type: 'session_subscribed', sessionId, requestId, historyRevision, historyChanged, isRunning }
|
||
```
|
||
|
||
## 3. 请求、响应与并发约束
|
||
|
||
- 首屏返回 `type='session_info'`、最近12条、`historyPending=false` 和会话元数据。
|
||
分页返回 `type='session_history_chunk'`,向前最多24条,`remaining=0`。
|
||
- 两者都提供 `sessionId/requestId/messages/historyTotal/historyBaseIndex/historyCursor/`
|
||
`historyAvailable/historySource/historyRevision`;`requestId` 最多160字符,浏览器生成
|
||
有界且唯一的代次标识,成功响应必须同时匹配会话和请求。
|
||
- 首屏的 `runtime` 是完整 `resume_generating` 快照或 `null`,即使没有 WebSocket
|
||
也能显示已产生输出。停止运行但尚未归档的保留态显示后须收束生成状态。
|
||
- HTTP 只读取 cc-web 自身已保存记录;首屏不扫描原生日志、不协调跨会话、不清未读。
|
||
归档按块有界读取;旧快照保留来源待核实状态。磁盘和 JSON 处理仍在同一服务进程,
|
||
传输拆分不代表所有同步 IO 已异步化。
|
||
- 订阅校验元数据、绑定实时输出、清未读并冲刷已有跨会话回复,不发送历史消息。
|
||
比较清未读前的归档修订;有差异时由浏览器再通过 HTTP 对账。确认后返回清未读
|
||
产生的新修订,避免元数据保存导致重复对账。
|
||
- 首屏、正文分页、目录分页和消息定位均不检查 WebSocket 是否已连接或鉴权。
|
||
WebSocket 关闭不能取消 HTTP 读取、清除分页状态或结束首屏加载。
|
||
- 新选择、同会话新代次和退出视图取消旧 HTTP;控制器、视图代次和活动分页对象
|
||
三重隔离迟到结果。错误恢复保留已有视图和分页游标,取消后清除加载标志。
|
||
- `session_subscribed` 和带请求标识的运行快照必须属于当前订阅或视图。
|
||
订阅触发的历史对账保留实时输出,不再应用较早的 HTTP runtime,也不重复订阅。
|
||
- 若查询期间已收到该会话的 `done`,弃用较早快照并重新查询终态;不能让完成后的
|
||
页面被旧 `isRunning=true` 快照覆盖。
|
||
- HTTP 45秒硬超时只限制真实请求;每个成功、失败或取消出口都清理计时器。
|
||
`Server-Timing` 和 `session_http_query` 记录请求、阶段、状态、数量和耗时,不记录正文。
|
||
|
||
## 4. 校验与错误矩阵
|
||
|
||
| 条件 | HTTP / 订阅行为 |
|
||
|---|---|
|
||
| 缺失或无效 Bearer | 401 `not_authenticated` |
|
||
| URI 解码失败或 ID 含字母数字、连字符之外字符 | 400 `history_invalid_id`;不得清洗后别名到另一会话 |
|
||
| 游标为负数、小数或非安全整数 | 400 `history_invalid_cursor` |
|
||
| 会话确实缺失 | 404 `session_not_found` |
|
||
| 元数据、清单或历史块读取失败 | 500 `history_read_failed`,禁止吞成404 |
|
||
| 实时绑定失败 | WS `session_subscribe_failed`,历史继续可读 |
|
||
| 新选择取消、迟到响应或不匹配订阅 | 不修改新视图及其加载状态 |
|
||
|
||
所有 HTTP 错误携带 `type='error'`、`code/message/sessionId/requestId`。未完成身份
|
||
校验或 URI 解码时 `sessionId` 可以为空,不泄露磁盘路径及消息内容。
|
||
|
||
## 5. 正常、基本与失败场景
|
||
|
||
- 正常:57条归档关闭 WS 后首屏12条、继续分页24条和21条,按原顺序且无重复。
|
||
- 基本:运行中会话在假 OPEN 时仍显示历史和完整运行输出,实时订阅独立恢复。
|
||
- 交错:HTTP 读完后任务完成,订阅修订变化触发 HTTP 对账,最终回复显示且无生成占位。
|
||
- 失败:A 响应晚于 B,A 的正文和运行态都不能覆盖 B;坏历史返回500并保留当前视图。
|
||
|
||
## 6. 必需回归与断言
|
||
|
||
- `scripts/session-http-load-frontend-unit.js` 执行生产请求函数:无 WS 读取、真实 abort、
|
||
忽略取消后依然到达的响应、同会话代次、分页取消、完成交错、500和超时计时器释放。
|
||
- `node scripts/regression.js --target session-http-load`:隔离真实服务,断 WS 后查询、
|
||
首屏/分页、鉴权、坏 ID/游标、损坏清单/块、独立订阅、修订变化和完整运行快照。
|
||
- `node scripts/session-http-load-regression.js --browser`:真实 Firefox 经转发层测试
|
||
假 OPEN、读取途中断 WS、完全断连后切换、分页、迟到 A 响应、完成间隙及历史500。
|
||
断言浏览器不发送 `load_session/load_history_page/resume_session` 查询历史。
|
||
- 外部 CDN 不稳定时可设置 `CC_WEB_BROWSER_CDN_DIR`,使用与页面相同版本的原始
|
||
`marked.min.js/highlight.min.js/mermaid.min.js/atom-one-dark.min.css` 本地夹具;仅转发层
|
||
改写这些静态 URL,生产请求函数与渲染器保持原样,避免把登录前静态资源超时当作切换失败。
|
||
- 所有数据、原生目录和 Codex home 锁路径必须隔离;不修改 HOME/CODEX_HOME 或生产锁。
|
||
单次测试最多60秒,不能用生产会话的读写代替隔离验证。
|
||
|
||
## 7. 错误与正确示例
|
||
|
||
```js
|
||
// 错误:已有令牌也必须先等长连接;假 OPEN 时只能等待业务超时。
|
||
if (ws.readyState === 1 && wsAuthenticated) ws.send(JSON.stringify({ type: 'load_session', sessionId }));
|
||
|
||
// 正确:历史独立读取,成功应用后再接入实时订阅。
|
||
const snapshot = await fetchSessionQuery(sessionId, requestId, { signal });
|
||
handleServerMessage(snapshot);
|
||
requestSessionResume(sessionId);
|
||
```
|
||
|
||
生产函数还必须执行控制器身份、请求代次和完成事件版本校验,不能仅复制此示意代码。
|