# 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()
}
1
2
3
4
5
6
7

createSession() 返回扩展后的 WebSession:

{
  id,
  title,
  budget,
  cost,
  stats(),
  engine,
  cleanup,
}
1
2
3
4
5
6
7
8
9

WS 协议新增:

browser → session_create / session_select / session_close
server  → sessions / status
1
2

前端用:

const histories = new Map(); // sessionId -> 展示消息
1

后端保存真实上下文,前端保存展示历史,切换时重绘。

# 四、深入追问(面试常见 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。