总览
读取侧 5 个函数 · 写入侧 7 个函数文件以 gateway-contract 的路径构建与响应解析函数为契约层,配合 react-query 做服务端状态、MMKV 做冷启动占位缓存。核心思路:让抽屉和聊天屏在冷启动瞬间先渲染上次数据,实时请求在后台补全。
use-session-history
缓存写入条件
类型定义
realtime WS
已实现的会话操作
全部基于 gateway-contract 的路径构建R查询类(读)
- fetchSessionsList — 分页列表,支持 search / channel / sortBy=updatedAt
- readPlaceholderSessions — 读 MMKV 占位数据(react-query placeholderData)
- fetchSession — 会话详情,404 → null
- fetchSessionActiveRun — active run 状态,无 key / 404 → { active:false }
- fetchSessionMessagePage — 历史消息分页(before 游标)
W动作类(写)
- createSession — 创建 + 注入新会话偏好(model / thinkingLevel)
- deleteSession — 删除
- renameSession — 重命名
- archiveSession / unarchiveSession — 归档 / 取消归档
- pinSession / unpinSession — 置顶 / 取消置顶
↑ createSession 还会从 preferences-store 读取按 gateway 保存的偏好(选中 agent、模型、思考级别),必要时对路由到的 agent 二次 PATCH initialAgentConfig。
缓存策略
MMKV 持久化 + react-query 内存态CMMKV 持久化缓存
- 列表:仅
offset===0 && !search时写入,按 profileId 分键 - 历史 / 详情:按 profileId + sessionKey 分键(每会话独立 scope)
- 统一 TTL 1 小时(query-cache.ts 默认)
Qreact-query 联动
- 动作后 → refreshList() → resetQueries(sessionsAll) 重拉第一页
- 聊天收尾 → mergeLatestSessionHistoryPage 就地合并 + invalidateSessionLists(仅标脏)
- 冷启动 → placeholderData 立即渲染,实时请求后台补全
// 缓存写入条件(fetchSessionsList 内)
if (offset === 0 && !search) {
writeCachedSessions(activeGatewayId, items); // 仅无过滤的第一页
}
改进建议
按优先级排序 · 前两条影响正确性-
动作函数校验响应体 ok / error
高delete / rename / archive 等只检查
res.ok(HTTP 状态)。但契约已提供parseSessionActionResponse/parseSessionRenameResponse,协议可能返回 HTTP 200 但 body{ ok:false, error:'…' },现在会被静默当成成功。const parsed = parseSessionRenameResponse(await res.json()); if (parsed.ok === false || parsed.renamed === false) { throw new Error(parsed.error ?? 'Rename failed'); }收益:消除静默失败;同一模式应用到全部 6 个动作函数。
-
删除 / 归档后清理该会话的详情与历史缓存
高列表缓存会在动作后经 refreshList 覆写,但 sessionDetail / sessionHistory 的 MMKV scope 不会同步清理。删除会话后,若用户冷启动并命中旧缓存,可能看到已删除会话的历史。
// deleteSession 成功后 clearCachedSessionDetail(profileId, key); // + 清除该 key 的 sessionHistory scope收益:避免删除/归档后冷启动渲染陈旧会话内容。
-
统一失效语义:invalidate vs refresh
中列表失效逻辑散落三处(infinite-list-sync / workspace-sync / use-chat-session),且语义不同:
invalidateSessionLists用refetchType:'none'只标脏不重拉。聊天发完一条消息后,抽屉的 updatedAt 排序可能不实时。收益:明确“发消息后需强制刷新排序”,而不是仅标脏;收敛为单一入口。
-
缓存写入的 channel 语义统一
中fetchSessionsList的 channel 默认是undefined → 'webchat',而 SessionsScreen 传null(全量)。缓存写入条件不区分 channel —— 若未来某调用方以 webchat 拉第一页,会覆盖全量缓存,抽屉数据范围漂移。// 建议:仅 channel === null(全量)时写入列表缓存 if (offset === 0 && !search && channel === null) { … }收益:缓存数据范围与“抽屉应显示的内容”保持一致。
-
时效敏感数据不应共用 1 小时 TTL
中active-run、archived / pinned 状态变化快,1 小时 TTL 会让冷启动看到过期状态。建议为这类数据提供更短的
maxAgeMs,或状态变化时主动 clear。收益:冷启动展示的状态更接近真实。
-
健壮性小修
低createSession 里“路由后补 PATCH”逻辑可读性略绕(用
if (!body.initialAgentConfig)判断);动作函数的 key 未统一 trim。可抽一个resolveAgentConfig辅助函数。收益:降低后续维护时的误解成本。
建议的行动顺序
- 1 先做 #1 响应体校验 — 改动小、收益确定,消除静默失败隐患。
- 2 再做 #2 缓存清理 — 删除/归档后同步清详情与历史缓存。
- 3 随后处理 #3 / #4 / #5 — 统一失效语义、channel 与 TTL 策略。
- 4 #6 随重构顺手做 — 不单独阻塞。