# Step 71: 状态栏与多会话管理
一句话导读:在 Web 版上加
SessionManager,让一个浏览器连接能创建、切换、关闭多个会话,并实时显示 token/cost/cache 状态。
# 一、这一步做了什么(What)
step71 新增 server/sessionManager.ts,把 Web 从“一个连接一个会话”升级成多会话工作台;server/session.ts 暴露 budget/cost/stats();wsServer.ts 扩展 session_create/select/close 和 status 协议;前端加左侧会话列表和顶部状态栏。
# 二、面试官视角:为什么要做?(Why)
面试题:WebSocket 已经能聊天,为什么还要多会话和状态栏?
因为真实 agent 工具不是一次性聊天框。用户会并行处理多个任务,需要切换上下文;同时 token、成本、缓存命中是 agent 的运行仪表盘,不能只藏在某一条 round 事件里。
| demo 聊天 | 工作台 |
|---|---|
| 单会话 | 多会话列表 |
| 状态散在消息里 | 顶部持续可见 |
| 关闭页面就丢 UI 历史 | 至少前端按 session 缓存展示 |
# 三、原理:它是怎么工作的(How)
SessionManager 的职责:
export class SessionManager {
async create()
select(id: string)
close(id: string)
list()
active()
}
2
3
4
5
6
7
createSession() 返回扩展后的 WebSession:
{
id,
title,
budget,
cost,
stats(),
engine,
cleanup,
}
2
3
4
5
6
7
8
9
WS 协议新增:
browser → session_create / session_select / session_close
server → sessions / status
2
前端用:
const histories = new Map(); // sessionId -> 展示消息
后端保存真实上下文,前端保存展示历史,切换时重绘。
# 四、深入追问(面试常见 follow-up)
Q:前端 histories Map 是真正持久化吗?
A:不是,只是 UI 展示缓存。真实上下文在后端 QueryEngine;真正 /resume 级别持久化要写 JSONL transcript。
Q:为什么 status 要服务端主动推?
A:token/cost 会在 round 后变化,忙闲状态也会变化。推送比前端轮询更贴合事件流模型。
Q:每个会话都 createCore 会不会重?
A:教学版简单隔离。真实系统会管理资源池、连接生命周期、MCP/LSP 复用等。
Q:会话标题怎么生成?
A:首次 prompt 后取前 24 字符作为标题,足够支撑列表扫描;真实系统可用模型或 transcript title。
# 五、踩坑 / 设计权衡
这一步仍没有跨进程持久化。它解决的是“一个 Web 进程内的多会话体验”,不是 Claude Code CLI /resume 那种全局 JSONL 恢复。
# 六、与真实源码的对照
| 我们的实现 | Claude Code 源码 |
|---|---|
SessionManager 管 WebSession | RemoteSessionManager / session registry |
status 推 token/cost/cache | cost-tracker + SDK status |
| 前端 Map 保存展示历史 | sessionStorage JSONL transcript |
| 简单 create/select/close | 远程订阅、重连、恢复、tombstone |
# 七、一句话总结
step71 把 Web 从“能聊天的页面”推进到“会话工作台”:多上下文可切换,成本和缓存状态可持续观察。
# 下一节预告
下一篇是 step72 源码对照③:把 step64-71 的 UI/Bridge/Session 路线映射回真实 SDK、Bridge、Remote 和 sessionStorage。