改用 HTTP 加载会话历史并更新 CentOS 7 发布包

This commit is contained in:
shiyue
2026-10-15 00:36:26 +08:00
parent 2b1c91488d
commit fb2c3360c9
22 changed files with 1466 additions and 175 deletions

View File

@@ -21,6 +21,7 @@ This directory contains guidelines for backend development. Fill in each file wi
| [Logging Guidelines](./logging-guidelines.md) | Structured logging, log levels | To fill |
| [Codex MCP 配置输出](./codex-mcp-config.md) | 旧类型配置兼容及 Codex 配置出口 | 已记录 |
| [完整历史保存](./session-history.md) | 完整档案、快照缓存、并发及分页契约 | 已记录 |
| [会话查询与实时订阅](./session-query.md) | HTTP 历史、运行快照、独立订阅与竞态隔离 | 已记录 |
---

View File

@@ -0,0 +1,100 @@
# 会话查询与实时订阅契约
## 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);
```
生产函数还必须执行控制器身份、请求代次和完成事件版本校验,不能仅复制此示意代码。

View File

@@ -1,5 +1,9 @@
# State Management
会话视图的 HTTP 读取、实时订阅、请求代次和完成事件交错规则,见
[会话查询与实时订阅契约](../backend/session-query.md)。历史读取不能依赖 WebSocket
连接、鉴权或重连;长连接断开不能取消正在进行的 HTTP 请求。
> How state is managed in this project.
---